Gav*_*orn 49 c# asp.net asp.net-web-api dotnet-httpclient
我正在编写一个使用ASP.NET Web API代理某些HTTP请求的应用程序,我正在努力识别间歇性错误的来源.这似乎是一种竞争条件......但我并不完全确定.
在我详细介绍之前,这里是应用程序的一般通信流程:
Proxy应用程序使用.NET 4.5在ASP.NET Web API RTM中编写.执行中继的代码如下所示:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
Run Code Online (Sandbox Code Playgroud)
间歇发生的错误是:
在异步操作仍处于挂起状态时完成异步模块或处理程序.
此错误通常发生在对代理应用程序的前几个请求之后,之后不会再看到错误.
Visual Studio在抛出时永远不会捕获异常.但是错误可以在Global.asax Application_Error事件中捕获.不幸的是,Exception没有Stack Trace.
代理应用程序托管在Azure Web角色中.
任何帮助确定罪魁祸首将不胜感激.
Ste*_*ary 65
你的问题是一个微妙的问题:async你传递给的lambda PushStreamContent被解释为一个async void(因为PushStreamContent构造函数只将Actions作为参数).所以你的模块/处理程序完成和async voidlambda 的完成之间存在竞争条件.
PostStreamContent检测流关闭并将其视为其结束Task(完成模块/处理程序),因此您只需要确保async void在流关闭后仍然没有可以运行的方法.async Task方法还可以,所以这应该解决它:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
Run Code Online (Sandbox Code Playgroud)
如果您希望代理扩展得更好,我还建议删除所有Result调用:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Run Code Online (Sandbox Code Playgroud)
您以前的代码会阻塞每个请求的一个线程(直到收到标头); 通过一直使用async到控制器级别,在此期间不会阻塞线程.
我想为遇到相同错误的其他人添加一些智慧,但您的所有代码似乎都很好。在发生这种情况的整个调用树中查找传递给函数的任何 lambda 表达式。
我在对 MVC 5.x 控制器操作的 JavaScript JSON 调用中收到此错误。我在堆栈中上下所做的一切都是async Task使用await.
但是,使用 Visual Studio 的“设置下一条语句”功能,我系统地跳过了几行以确定是哪一行导致了它。我一直深入研究本地方法,直到调用外部 NuGet 包。被调用的方法将 anAction作为参数,并且为此 Action 传入的 lambda 表达式前面是async关键字。正如 Stephen Cleary 在上面的回答中指出的那样,这被视为async voidMVC 不喜欢的 。幸运的是,包有相同方法的 *Async 版本。切换到使用这些,以及对同一个包的一些下游调用解决了这个问题。
我意识到这不是该问题的新颖解决方案,但我在尝试解决该问题的搜索中多次通过此线程,因为我认为我没有任何电话async void或async <Action>电话,我想帮助其他人避免这种情况.
| 归档时间: |
|
| 查看次数: |
45039 次 |
| 最近记录: |