ASP.NET Web API消息处理程序

时间:2012-04-20 17:50:12

标签: asp.net-web-api dotnet-httpclient

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

作为一个例子,我可能希望首先执行日志记录处理程序,因此我总是记录一个请求。

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

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

3 个答案:

答案 0 :(得分:8)

您注册处理程序的顺序决定了它们何时被调用,但是当Aliostad指出它们在俄罗斯玩偶模型中工作时,所以第一个也称为最后一个,依此类推。

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

答案 1 :(得分: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的{​​{1}}方法(以前称为System.Net.Http.HttpClientFactory方法,如Ali指出的那样。)HttpPipelineFactory.Create方法接受两个参数:CreatePipelineHttpMessageHandlerIEnumerable<DelegatingHandler>方法将HttpServer.Initialize传递给System.Web.Http.Dispatcher.HttpControllerDispatcher参数作为链中的最后HttpMessageHandlerHttpMessageHandler参数的HttpConfiguration.MessageHandlers。

IEnumerable<DelegatingHandler>方法内部发生的事情非常聪明IMO:

CreatePipeline

正如您所看到的,消息处理程序顺序是相反的,Matryoshka doll已创建但在此处要小心:确保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;
        });
    }
}

由于我们提供了continuation,我们的消息处理程序将在返回到FILO顺序的客户端的路上回调。因此,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; }); } } 中的延续方法将是第一个在返回途中被调用的方法,而MyMessageHandler2内的连续方法将是第二个方法。

答案 2 :(得分: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;
    }

所以代码的作用是获取一个列表,然后将其转换为俄罗斯娃娃