ここでは、Codeplex ASP.NET Web Stack リポジトリで入手できる最新のビットに基づいて話しています。
順序はユーザーによって制御され、任意の順序はありません。説明させてください:
と の 2 つのメッセージ ハンドラがある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 です。
フレームワークの内部を少し見てみるとInitialize
、インスタンスのメソッドがメソッド ofHttpServer
を呼び出していることがわかります (以前は Ali が示したようにメソッドとして知られていました) メソッドは と の 2 つのパラメーターを受け入れます。メソッドは、チェーン内の最後としてパラメーターを渡し、パラメーターとして HttpConfiguration.MessageHandlers を渡します。CreatePipeline
System.Net.Http.HttpClientFactory
HttpPipelineFactory.Create
CreatePipeline
HttpMessageHandler
IEnumerable<DelegatingHandler>
HttpServer.Initialize
System.Web.Http.Dispatcher.HttpControllerDispatcher
HttpMessageHandler
HttpMessageHandler
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;
}
ご覧のとおり、メッセージ ハンドラーの順序が逆になり、マトリョーシカ人形が作成されますが、ここで注意してください。これがHttpControllerDispatcher
チェーン内で実行される最後のメッセージ ハンドラーであることが保証されます。
2 回呼び出しの問題については、実際にはまったく正しくありません。メッセージ ハンドラは 2 回呼び出されることはありませんが、提供する継続メソッドは 2 回呼び出されます。それを実現するのはあなた次第です。コールバック (つまり継続) を提供すると、クライアントに戻る途中でメッセージ ハンドラーが呼び出され、再生できる生成された応答メッセージが返されます。
たとえば、次の 2 つが上記で登録したメッセージ ハンドラーであるとします。
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;
});
}
}
そして、これはもう1つです:
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
戻る途中で呼び出される最初のメソッドになり、 内のメソッドMyMessageHandler
は 2 番目になります。