我正在 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 的形式返回?
我也为此苦苦挣扎。 ReadAsStringAsync
和ReadAsAsync
返回任务对象。 引用 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
- Request.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin)
- 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 包含在正文中