我在这里谈论的是基于最新的位
Codeplex ASP.NET Web Stack 存储库。
顺序由用户控制,这里没有任意顺序。让我解释一下:
假设我们有两个消息处理程序:MyMessageHandler 和 MyMessageHandler2。假设我们将它们注册如下:
protected void Application_Start(object sender, EventArgs e) {
RouteConfig.RegisterRoutes(GlobalConfiguration.Configuration.Routes);
GlobalConfiguration.Configuration.MessageHandlers.Add(new MyMessageHandler());
GlobalConfiguration.Configuration.MessageHandlers.Add(new MyMessageHandler2());
}
您在这里期望MyMessageHandler 首先运行,MyMessageHandler2 作为第二个运行,换句话说,FIFO。
如果我们稍微深入了解一下框架内部,我们会发现HttpServer 实例的Initialize 方法正在调用System.Net.Http.HttpClientFactory 的CreatePipeline 方法(以前称为HttpPipelineFactory.Create Ali 指出的方法。)CreatePipeline 方法接受两个参数:HttpMessageHandler 和 IEnumerable<DelegatingHandler>。 HttpServer.Initialize 方法将 System.Web.Http.Dispatcher.HttpControllerDispatcher 传递给 HttpMessageHandler 参数作为链内的最后一个 HttpMessageHandler ,并将 HttpConfiguration.MessageHandlers 传递给 IEnumerable<DelegatingHandler> 参数。
CreatePipeline 方法内部发生的事情是非常聪明的 IMO:
public static HttpMessageHandler CreatePipeline(HttpMessageHandler innerHandler, IEnumerable<DelegatingHandler> handlers)
{
if (innerHandler == null)
{
throw Error.ArgumentNull("innerHandler");
}
if (handlers == null)
{
return innerHandler;
}
// Wire handlers up in reverse order starting with the inner handler
HttpMessageHandler pipeline = innerHandler;
IEnumerable<DelegatingHandler> reversedHandlers = handlers.Reverse();
foreach (DelegatingHandler handler in reversedHandlers)
{
if (handler == null)
{
throw Error.Argument("handlers", Properties.Resources.DelegatingHandlerArrayContainsNullItem, typeof(DelegatingHandler).Name);
}
if (handler.InnerHandler != null)
{
throw Error.Argument("handlers", Properties.Resources.DelegatingHandlerArrayHasNonNullInnerHandler, typeof(DelegatingHandler).Name, "InnerHandler", handler.GetType().Name);
}
handler.InnerHandler = pipeline;
pipeline = handler;
}
return pipeline;
}
如您所见,消息处理程序的顺序颠倒了,Matryoshka doll 被创建,但这里要小心:确保HttpControllerDispatcher 是在链中运行的最后一个消息处理程序。
关于调用两次的问题,实际上并不完全正确。消息处理程序不会被调用两次,另一方面,您将提供的延续方法将被调用。由你来实现它。如果您提供回调(换句话说,延续),您的消息处理程序将在返回客户端的途中被调用,并生成您可以使用的响应消息。
例如,假设下面两个是我们上面注册的消息处理器:
public class MyMessageHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) {
//inspect request here
return base.SendAsync(request, cancellationToken).ContinueWith(task => {
//inspect the generated response
var response = task.Result;
return response;
});
}
}
这是另一个:
public class MyMessageHandler2 : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) {
//inspect request here
return base.SendAsync(request, cancellationToken).ContinueWith(task => {
//inspect the generated response
var response = task.Result;
return response;
});
}
}
正如我们提供的延续,我们的消息处理程序将在返回客户端的途中以 FILO 顺序被回调。因此,MyMessageHandler2 中的 continuation 方法将是返回的第一个方法,MyMessageHandler 中的方法将是第二个方法。