5

カスタム メッセージ ハンドラの実行順序を制御することはできますか?

たとえば、ロギング ハンドラを最初に実行する場合は、常にリクエストをログに記録します。

ロギング ハンドラを最後に追加する以外に、これを達成する方法がわかりません。

config.MessageHandlers.Add(new CustomHandlerOne()); 
config.MessageHandlers.Add(new CustomHandlerTwo());
config.MessageHandlers.Add(new LoggingHandler());
4

3 に答える 3

8

ハンドラーを登録する順序によって、ハンドラーがいつ呼び出されるかが決まりますが、Aliostad が指摘しているように、ハンドラーはロシアの人形モデルで動作するため、最初のハンドラーは最後のハンドラーとしても呼び出されます。

登録されたハンドラーは、着信パスではボトムアップ方式で呼び出され、発信パスではトップダウン方式で呼び出されます。つまり、最後のエントリは、着信要求メッセージの場合は最初に呼び出されますが、発信応答メッセージの場合は最後に呼び出されます。

于 2012-04-21T13:38:10.513 に答える
4

ここでは、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 を渡します。CreatePipelineSystem.Net.Http.HttpClientFactoryHttpPipelineFactory.CreateCreatePipelineHttpMessageHandlerIEnumerable<DelegatingHandler>HttpServer.InitializeSystem.Web.Http.Dispatcher.HttpControllerDispatcherHttpMessageHandlerHttpMessageHandlerIEnumerable<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 番目になります。

于 2012-06-17T23:06:50.160 に答える
3

いいえ-AFAIK。

これはロシア人形モデルで、1 つのハンドラーが別のハンドラーの中にいて、最後のハンドラーが作業を行います。これは内部クラスに組み込まれてHttpPipelineFactoryいます (リリースされたソース コードを表示できます)。

    public static HttpMessageHandler Create(IEnumerable<DelegatingHandler> handlers, HttpMessageHandler innerChannel)
    {
        if (innerChannel == null)
        {
            throw Error.ArgumentNull("innerChannel");
        }

        if (handlers == null)
        {
            return innerChannel;
        }

        // Wire handlers up
        HttpMessageHandler pipeline = innerChannel;
        foreach (DelegatingHandler handler in handlers)
        {
            if (handler == null)
            {
                throw Error.Argument("handlers", SRResources.DelegatingHandlerArrayContainsNullItem, typeof(DelegatingHandler).Name);
            }

            if (handler.InnerHandler != null)
            {
                throw Error.Argument("handlers", SRResources.DelegatingHandlerArrayHasNonNullInnerHandler, typeof(DelegatingHandler).Name, "InnerHandler", handler.GetType().Name);
            }

            handler.InnerHandler = pipeline;
            pipeline = handler;
        }

        return pipeline;
    }

コードが行うことは、リストを取得してそれをRussian Dollに変換することです。

于 2012-04-20T18:10:47.033 に答える