【问题标题】:How do I get the raw request body from the Request.Content object using .net 4 api endpoint如何使用 .net 4 api 端点从 Request.Content 对象获取原始请求正文
【发布时间】:2016-02-23 22:29:28
【问题描述】:

我正在尝试捕获原始请求数据以进行问责,并希望将请求正文内容从请求对象中提取出来。

我看到有人建议做一个 Request.InputStream,但这个方法在 Request 对象上不可用。

知道如何获取 Request.Content 正文的字符串表示形式吗?

【问题讨论】:

    标签: c# asp.net-web-api


    【解决方案1】:

    在您对@Kenneth 的回答的评论中,您说ReadAsStringAsync() 正在返回空字符串。

    那是因为您(或模型绑定器之类的东西)已经阅读了内容,所以 Request.Content 中内部流的位置在末尾。

    你可以这样做:

    public static string GetRequestBody()
    {
        var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
        bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
        var bodyText = bodyStream.ReadToEnd();
        return bodyText;
    }
    

    【讨论】:

    • 这段代码在 HTTP 中工作正常,但是当我使用 HTTPS 连接时,它在正文中给出了空字符串,你能帮我吗?
    • 奇怪,对我来说它甚至可以在 HTTPS 连接上工作。
    • 当我从 HTTPS 调用时获取空字符串,我需要在 web 配置中添加任何内容吗?
    • 如果您想知道“.Current”属性在 HttpContext 中不可用,它是由于架构错误而被删除的。解释可以在这里找到:stackoverflow.com/a/38574489/4100453
    【解决方案2】:

    您可以通过在Request.Content 属性上调用ReadAsStringAsAsync 来获取原始数据。

    string result = await Request.Content.ReadAsStringAsync();
    

    如果你希望它在一个字节或一个流中,有各种重载。由于这些是异步方法,您需要确保您的控制器是异步的:

    public async Task<IHttpActionResult> GetSomething()
    {
        var rawMessage = await Request.Content.ReadAsStringAsync();
        // ...
        return Ok();
    }
    

    编辑:如果你从这个方法接收到一个空字符串,这意味着其他东西已经读取了它。当它这样做时,它将指针留在最后。另一种方法如下:

    public IHttpActionResult GetSomething()
    {
        var reader = new StreamReader(Request.Body);
        reader.BaseStream.Seek(0, SeekOrigin.Begin); 
        var rawMessage = reader.ReadToEnd();
    
        return Ok();
    }
    

    在这种情况下,您的端点不需要是异步的(除非您有其他异步方法)

    【讨论】:

    • 谢谢 Kenneth,但是 rawMessage 返回一个空字符串,我做对了吗?我可以看到内容包含数据,为什么 ReadAsStringAsync() 返回一个空字符串?
    • 与@MarkKadlec 相同的问题,此解决方案不起作用
    • 检查你在ReadAsStringAsync之前是否已经阅读过。这是一个流,此方法将读取到末尾并将指针留在那里。
    • 你不能在 Request.Body 流上寻找。
    • 如果您正在调试并希望在 Watch 窗口中以字符串形式查看内容。使用这个:Request.Content.ReadAsStringAsync().Result
    【解决方案3】:

    对于其他不想让他们的控制器异步,或者无法访问 HttpContext,或者正在使用 dotnet 核心(这个答案是我在谷歌上找到的第一个尝试这样做的答案)的其他用户,以下对我有用:

    [HttpPut("{pathId}/{subPathId}"),
    public IActionResult Put(int pathId, int subPathId, [FromBody] myViewModel viewModel)
    {
    
        var body = new StreamReader(Request.Body);
        //The modelbinder has already read the stream and need to reset the stream index
        body.BaseStream.Seek(0, SeekOrigin.Begin); 
        var requestBody = body.ReadToEnd();
        //etc, we use this for an audit trail
    }
    

    【讨论】:

    • 重要:如果你的方法没有参数,那么不要使用Seek 否则你会得到“NotSupportedException: Specified method is not supported. Microsoft.AspNetCore.Server.Kestrel.Core.Internal .Http.HttpRequestStream.Seek"
    【解决方案4】:

    如果您既需要从请求中获取原始内容,又需要在控制器中使用它的绑定模型版本,您很可能会遇到此异常。

    NotSupportedException: Specified method is not supported. 
    

    例如,您的控制器可能看起来像这样,让您想知道为什么上面的解决方案不适合您:

    public async Task<IActionResult> Index(WebhookRequest request)
    {
        using var reader = new StreamReader(HttpContext.Request.Body);
    
        // this won't fix your string empty problems
        // because exception will be thrown
        reader.BaseStream.Seek(0, SeekOrigin.Begin); 
        var body = await reader.ReadToEndAsync();
    
        // Do stuff
    }
    

    您需要将模型绑定从方法参数中取出,然后手动绑定自己:

    public async Task<IActionResult> Index()
    {
        using var reader = new StreamReader(HttpContext.Request.Body);
    
        // You shouldn't need this line anymore.
        // reader.BaseStream.Seek(0, SeekOrigin.Begin);
    
        // You now have the body string raw
        var body = await reader.ReadToEndAsync();
    
        // As well as a bound model
        var request = JsonConvert.DeserializeObject<WebhookRequest>(body);
    }
    

    很容易忘记这一点,我以前解决过这个问题,但现在不得不重新学习解决方案。希望我在这里的回答能给自己一个很好的提醒……

    【讨论】:

      【解决方案5】:

      这里是this answer 作为扩展方法:

      using System.IO;
      using System.Text;
      
      namespace System.Web.Http
      {
          public static class ApiControllerExtensions
          {
              public static string GetRequestBody(this ApiController controller)
              {
                  using (var stream = new MemoryStream())
                  {
                      var context = (HttpContextBase)controller.Request.Properties["MS_HttpContext"];
                      context.Request.InputStream.Seek(0, SeekOrigin.Begin);
                      context.Request.InputStream.CopyTo(stream);
                      var requestBody = Encoding.UTF8.GetString(stream.ToArray());
                      return requestBody;
                  }
              }
          }
      }
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-07
        • 2013-09-13
        • 1970-01-01
        • 1970-01-01
        • 2022-01-13
        • 2016-04-24
        • 2021-03-07
        • 1970-01-01
        相关资源
        最近更新 更多