【问题标题】:Getting raw POST data from Web API method从 Web API 方法获取原始 POST 数据
【发布时间】:2012-10-24 23:26:15
【问题描述】:

我在 ApiController 类中有以下 Web API 方法:

public HttpResponseMessage Post([FromBody]byte[] incomingData)
{
  ...
}

我希望 incomingData 成为 POST 的原始内容。但似乎 Web API 堆栈尝试使用 JSON 格式化程序解析传入的数据,这导致客户端的以下代码失败:

new WebClient().UploadData("http://localhost:15134/api/Foo", new byte[] { 1, 2, 3 });

有没有简单的解决方法?

【问题讨论】:

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


    【解决方案1】:

    在代码块下面的控制器中获取请求正文原始有效负载

     string requestBody = string.Empty;
            using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
            {
                requestBody = await reader.ReadToEndAsync();
            }
    

    【讨论】:

      【解决方案2】:

      或者干脆

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

      确保在处理原始请求之前在 THE SAME THREAD 上运行上述行

      注意:这是针对 ASP.NET MVC 5

      【讨论】:

      • 这是该线程中唯一对我有用的答案。谢谢!
      【解决方案3】:

      在 MVC 6 中,请求似乎没有“内容”属性。这是我最终做的:

      [HttpPost]
      public async Task<string> Post()
      {
          string content = await new StreamReader(Request.Body).ReadToEndAsync();
          return "SUCCESS";
      }
      

      【讨论】:

        【解决方案4】:

        我接受了 LachlanB 的回答,并将它放在一个实用程序类中,其中包含一个我可以在所有控制器中使用的静态方法。

        public class RawContentReader
        {
            public static async Task<string> Read(HttpRequestMessage req)
            {
                using (var contentStream = await req.Content.ReadAsStreamAsync())
                {
                    contentStream.Seek(0, SeekOrigin.Begin);
                    using (var sr = new StreamReader(contentStream))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }
        

        然后我可以通过这种方式从我的任何 ApiController 的方法中调用它:

        string raw = await RawContentReader.Read(this.Request);
        

        【讨论】:

          【解决方案5】:

          其他答案建议删除输入参数,但这会破坏您现有的所有代码。要正确回答这个问题,一个更简单的解决方案是创建一个如下所示的函数(感谢下面的 Christoph 提供此代码):

          private async Task<String> getRawPostData()
          {
              using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
              {
                  contentStream.Seek(0, SeekOrigin.Begin);
                  using (var sr = new StreamReader(contentStream))
                  {
                      return sr.ReadToEnd();
                  }
              }
          }
          

          然后在你的 web api 调用中获取原始发布的数据,如下所示:

          public HttpResponseMessage Post ([FromBody]byte[] incomingData)
          {
              string rawData = getRawPostData().Result;
          
              // log it or whatever
          
              return Request.CreateResponse(HttpStatusCode.OK);
          }
          

          【讨论】:

          • 干得好,谢谢!我正在添加一个答案,将您的答案反刍为具有静态方法的实用程序类。
          • 如果你像这样访问 Task.Result 属性,你会立即在线程上等待。在这里使用任务有什么好处吗?
          • @codinglifestyle 我认为你必须返回一个 Task 因为你正在调用 ReadAsStreamAsync() 这是一个异步方法。
          • @Rocklan 我在 .Seek 调用中得到:“不支持指定的方法”,因此必须是特定于版本的。
          【解决方案6】:

          如果除了模型参数之外还需要原始输入以便于访问,您可以使用以下内容:

          using (var contentStream = await this.Request.Content.ReadAsStreamAsync())
          {
              contentStream.Seek(0, SeekOrigin.Begin);
              using (var sr = new StreamReader(contentStream))
              {
                  string rawContent = sr.ReadToEnd();
                  // use raw content here
              }
          }
          

          秘密是在尝试读取数据之前使用stream.Seek(0, SeekOrigin.Begin) 重置流。

          【讨论】:

          • 谢谢你,我已经根据你的代码发布了一个答案,如果你想合并它,我会删除我的答案。
          • System.NotSupportedException:此流不支持搜索操作。
          • 你使用的是什么环境?
          • @ChristophHerold 我收到了 Jerermy 的 NotSupportedException 错误。我是带有 Web API MVC(非核心)的 .NET 4.7.2。此外,无论如何,我的 rawContent 始终是空的。您是否知道任何可能限制阅读的内容?我的模型按预期设置,所以这很疯狂,因为内容显然在那里,并且内容大小也设置正确,但无法理解为什么它总是空的,并且想知道 web.config 或类似内容中的任何内容是否可能会阻塞这?我以为是因为我在属性类中尝试这样做,但事实并非如此。
          • @Thierry 我刚刚创建了一个简单的示例 .NET 4.7.2 Web API MVC 项目,在ValuesController 中,我将我的代码添加到了Post 方法中。对我来说,它就像一个魅力。一定有其他东西干扰了您的应用程序。我从.ReadAsStreamAsync 调用中获得的流类型是System.Net.Http.StreamContent.ReadOnlyStream。也许,你可以检查你得到什么类型的流。这有助于缩小范围。
          【解决方案7】:

          对于遇到此问题的其他人,解决方案是定义不带参数的 POST 方法,并通过 Request.Content 访问原始数据:

          public HttpResponseMessage Post()
          {
            Request.Content.ReadAsByteArrayAsync()...
            ...
          

          【讨论】:

          • 我需要类似的东西,但对于字符串,确保 POST 方法中没有参数对于使用等效的 ReadAsStringAsync 是必不可少的,否则它是空的。
          • 这是我发现的最简单的方法。我们的合作伙伴不断添加他们希望我们使用的新的未记录字段,但我们永远不知道他们如何拼写/格式化它们。谢谢!
          • 谢谢。在 MVC6 中,为了获得完整的正文,我不得不删除参数并手动读取 Request.Body。
          猜你喜欢
          • 1970-01-01
          • 2013-07-26
          • 2018-09-01
          • 2017-08-31
          • 2018-01-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多