无法在ASP.NET WebApi控制器中读取Request.Content

时间:2012-04-12 16:32:20

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

我在TransferMode.Streamed HttpSelfHostConfiguration exe中使用WebApi编写代理。

当我使用fiddler发布到我的ApiController时,出于某种原因我无法读取Request.Content - 即使我有POST数据也会返回“”

public class ApiProxyController : ApiController
{

    public Task<HttpResponseMessage> Post(string path)
    {
        return Request.Content.ReadAsStringAsync().ContinueWith(s =>
        {
            var content = new StringContent(s.Result); //s.Result is ""
                CopyHeaders(Request.Content.Headers, content.Headers);
            return Proxy(path, content);
        }).Unwrap();
    }

    private Task<HttpResponseMessage> Proxy(string path, HttpContent content)
    {
        ...
    }
}

这是我的网络请求

POST http://localhost:3001/api/values HTTP/1.1
Host: localhost:3001
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Type: application/json
Content-Length: 26

{ "text":"dfsadfsadfsadf"}

我做错了什么?为什么s.Result作为空字符串而不是原始json返回?

10 个答案:

答案 0 :(得分:35)

我也很挣扎。 ReadAsStringAsyncReadAsAsync返回一个任务对象。引用Result属性将返回内容。它可能是引用Result属性导致异步读取请求被阻止。

示例:

string str = response.Content.ReadAsStringAsync().Result;

答案 1 :(得分:17)

此帖子的签名会占用帖子数据:

public HttpResponseMessage Post([FromBody]string postdata)

将其更改为:

public HttpResponseMessage Post()

然后此调用可以正常获取帖子数据:

string str = response.Content.ReadAsStringAsync().Result;

自我测试。使用第一个签名,str为空,使用第二个str有帖子数据!

答案 2 :(得分:17)

我意识到这已经过时了,而且已经得到了解答,但是对于它的价值,你不能使用ReadAsStringAsync()的原因并不是因为它像所建议的那样'吃掉数据',这是因为内容正在作为流处理,并且由于数据已被消息格式化程序使用,因此流的位置已经在最后。

要使用ReadAsStringAsync(),首先需要将内容流位置重置为开头。

我是这样做的:response.RequestMessage.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin )因为我只有HttpResponseMessage,但是如果您可以直接访问HttpRequestMessage(就像你在Controller中那样),你可以使用功能相同的Request.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin )我想。

延迟修改

如上所述使用Result读取异步流将在许多情况下导致死锁和阻塞线程。如果必须以同步方式从异步流中读取,最好使用以下格式:

 new TaskFactory( CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default )
      .StartNew<Task<TResult>>( func )
      .Unwrap<TResult>()
      .GetAwaiter()
      .GetResult();

其中func是您要运行的异步操作,因此在这种情况下它将类似于async () => { await Request.Content.ReadAsStreamAsync(); } ...这样您就可以将方法的异步部分放在{{1}中部分并正确解开编组回同步代码时发生的任何异常。

更好的是,让整个堆栈异步。

答案 3 :(得分:5)

我相信你对使用Request.Content的ApiController说得对。您在ApiController中看到的“Request”对象实际上是System.Net.Http.HttpRequestMessage类型。我能够解决这个问题,但备份到System.Web.HttpRequest对象,如:

Dim content as string
If HttpContext.Current.Request.InputStream.CanSeek Then
    HttpContext.Current.Request.InputStream.Seek(0, IO.SeekOrigin.Begin)
End If
Using reader As New System.IO.StreamReader(HttpContext.Current.Request.InputStream)
    content = reader.ReadToEnd()
End Using

我不知道寻求倒带是否有必要,但我把它放在以防万一。

答案 4 :(得分:4)

我最终通过继承基本接口而不是ApiController来实现这一点 - 我认为ApiController是正在吃响应的模型绑定

编辑:构建代理的正确方法是MessageHandler,而不是ApiController

答案 5 :(得分:2)

  1. Request.Content.ReadAsStreamAsync()。Result.Seek(0,System.IO.SeekOrigin.Begin)
  2. new System.IO.StreamReader(Request.Content.ReadAsStreamAsync()。Result).ReadToEnd()

答案 6 :(得分:0)

尝试将ReadAsStringAsync()替换为ReadAsAsync<string>()

答案 7 :(得分:0)

您应该为您的参数使用复杂类型,然后在正文中使用一些json,如

{路径:“c:...”}

Als使用

Content-Type:application / json; charset = UTF-8

你的帖子请求中的

标题,以便web api知道json包含在正文中

答案 8 :(得分:0)

此问题的最新补充说明了如何从WebAPI读取POST数据:

<!DOCTYPE html>
<html>

<body>
    Title:
    <input id="userTitle" type="text" name="lname"></input>
    <button onclick="changeTitle()">Change Title</button>
    <script>
        function changeTitle() {
            document.title = document.getElementById('userTitle').value;
        }
    </script>
</body>

</html>
​

答案 9 :(得分:0)

尝试使用 CopyToAsync 而不是 ReadAsStringAsync 似乎可以解决问题

var ms = new MemoryStream();
await response.Content.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);

var sr = new StreamReader(ms);
responseContent = sr.ReadToEnd();