25

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

当我使用提琴手发布到我的 ApiController 时,由于某种原因我无法读取 Request.Content - 即使我已经发布了数据,它也会返回“”

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 形式返回?

4

10 回答 10

35

我也为此苦苦挣扎。ReadAsStringAsyncReadAsAsync返回一个任务对象。引用该Result属性会返回内容。它可能是引用 Result 属性导致异步读取请求阻塞。

例子:

string str = response.Content.ReadAsStringAsync().Result;
于 2012-09-05T15:13:05.103 回答
20

我意识到这是旧的,并且已经得到回答,但是对于它的价值,你不能使用的原因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(); }……这样,您可以将方法的异步部分放在该StartNew部分内,并正确解开编组回同步代码时发生的任何异常。

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

于 2016-01-14T09:59:57.717 回答
18

这个 post 签名吃掉了 post 数据:

public HttpResponseMessage Post([FromBody]string postdata)

将其更改为:

public HttpResponseMessage Post()

那么这个调用可以很好地获取发布数据:

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

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

于 2014-06-27T04:20:59.997 回答
5

我相信您对 ApiController 吃 Request.Content 的看法是正确的。您在 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

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

于 2013-05-08T21:04:41.043 回答
4

我最终通过从基本接口而不是 ApiController 继承来完成这项工作 - 我认为 ApiController 是模型绑定,它正在吃掉响应

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

于 2012-04-16T21:44:54.883 回答
2
  1. Request.Content.ReadAsStreamAsync().Result.Seek(0, System.IO.SeekOrigin.Begin)
  2. 新 System.IO.StreamReader(Request.Content.ReadAsStreamAsync().Result).ReadToEnd()
于 2017-11-16T12:30:41.217 回答
1

This late addition to the answers here show how to read the POST data from WebAPI:

string postData;
using (var stream = await request.Content.ReadAsStreamAsync())
{
    stream.Seek(0, SeekOrigin.Begin);
    using (var sr = new StreamReader(stream))
    {
        postData = await sr.ReadToEndAsync();
    }
}
于 2019-12-31T20:07:39.800 回答
0

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

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

阿尔斯使用

内容类型:应用程序/json;字符集=UTF-8

您的 post 请求中的标头,以便 web api 知道 json 包含在正文中

于 2012-11-30T10:38:43.877 回答
0

尝试替换ReadAsStringAsync()ReadAsAsync<string>().

于 2012-04-16T20:18:19.737 回答
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();
于 2021-04-27T08:55:31.893 回答