无法读取 WebAPI 控制器 ASP.NET Request.Content



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

当我使用提琴手发布到我的 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 的形式返回?

我也为此苦苦挣扎。 ReadAsStringAsyncReadAsAsync返回任务对象。 引用 Result 属性将返回内容。它可能引用了 Result 属性,导致异步读取请求被阻止。

例:

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

我意识到这很旧,并且已经得到了回答,但就其价值而言,您不能使用 ReadAsStringAsync() 的原因不是因为它像建议的那样"吃掉数据",而是因为内容正在作为流进行处理,并且由于数据已被消息格式化程序使用,因此流的位置已经在末尾。

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

我是这样做的:response.RequestMessage.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin )因为我只有HttpResponseMessage,但是如果你可以直接访问HttpRequestMessage(就像你在控制器中所做的那样),你可以使用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(); }...通过这种方式,您可以将方法的异步部分放在StartNew部分中,并正确解包在编组回同步代码时发生的任何异常。

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

这个帖子签名吃掉了帖子数据:

public HttpResponseMessage Post([FromBody]string postdata)

将其更改为:

public HttpResponseMessage Post()

然后,此调用可以正常工作,以获取 POST 数据:

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

我自己测试了一下。 使用第一个签名,str 为空,使用第二个 str 有 post 数据!

我相信

你对ApiController吃Request.Content的看法是正确的。 您在 ApiController 中看到的"请求"对象实际上是 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

我不知道是否需要搜索倒带,但我把它放进去以防万一。

我最终通过从基本接口而不是 ApiController 继承来解决这个问题 - 我认为 ApiController 是模型绑定,它正在吃响应

编辑:构建代理的正确方法是消息处理程序,而不是 ApiController

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

这里对答案的后期补充展示了如何从 WebAPI 读取 POST 数据:

string postData;
using (var stream = await request.Content.ReadAsStreamAsync())
{
    stream.Seek(0, SeekOrigin.Begin);
    using (var sr = new StreamReader(stream))
    {
        postData = await sr.ReadToEndAsync();
    }
}

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

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

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

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

{ 路径: "c:..." }

阿尔斯使用

内容类型:application/json;charset=UTF-8

POST 请求中的标头,以便 Web API 知道 JSON 包含在正文中

最新更新