5

是否可以控制自定义消息处理程序的执行顺序?

例如,我可能希望首先执行一个日志处理程序,所以我总是记录一个请求。

除了最后添加日志处理程序之外,我没有看到如何实现这一点。

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

3 回答 3

8

您注册处理程序的顺序决定了它们何时被调用,但正如 Aliostad 指出的那样,它们在俄罗斯娃娃模型中工作,因此第一个 in 也称为最后一个 out,依此类推。

注册的处理程序在传入路径中以自下而上的方式调用,在传出路径中以自上而下的方式调用。也就是说,对于传入的请求消息,首先调用最后一个条目,但对于传出的响应消息,最后调用最后一个条目。

于 2012-04-21T13:38:10.513 回答
4

我在这里基于 Codeplex ASP.NET Web Stack repo 上可用的最新位进行讨论。

顺序由用户控制,这里没有任意顺序。让我解释:

假设我们有两个消息处理程序:MyMessageHandlerMyMessageHandler2. 假设我们将它们注册如下:

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方法HttpServer正在调用CreatePipeline方法的方法System.Net.Http.HttpClientFactory(以前称为HttpPipelineFactory.Create方法,如阿里所说。)CreatePipeline方法接受两个参数:HttpMessageHandlerIEnumerable<DelegatingHandler>HttpServer.Initialize方法将参数作为链中的最后一个传递System.Web.Http.Dispatcher.HttpControllerDispatcher,并将 HttpConfiguration.MessageHandlers 传递给参数。HttpMessageHandlerHttpMessageHandlerIEnumerable<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是在链中运行的最后一个消息处理程序。

至于调用两次的问题,实际上并不完全正确。消息处理程序不会被调用两次,另一方面,您将提供的延续方法将被调用。由你来实现它。如果您提供回调(换句话说,延续),您的消息处理程序将在返回客户端的途中被调用,并生成您可以使用的响应消息。

例如,我们假设以下两个是我们上面注册的消息处理程序:

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将是第一个在返回途中被调用的方法,而内部的延续方法MyMessageHandler将是第二个。

于 2012-06-17T23:06:50.160 回答
3

不 - AFAIK。

这是俄罗斯娃娃模型,一个处理程序在另一个处理程序中,直到最后一个处理工作。这是内置在内部类中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;
    }

所以代码所做的就是得到一个列表,然后把它变成一个俄罗斯娃娃

于 2012-04-20T18:10:47.033 回答