2012-04-20 88 views
5

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

作为一个例子,我可能需要一个日志处理程序先执行,所以我总是记录一个请求。

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

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

回答

7

在您注册的处理程序的顺序决定时,他们被称为但是Aliostad指出,他们在俄罗斯套娃模型工作,所以也被称为最后一个出去等等的第一个。

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

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; 
    } 

那么什么代码所做的是得到一个列表,然后把它变成一个俄罗斯套娃

+0

这就是我的想法。我认为它也不支持提供者的概念。像MVC中的IFilterProvider接口一样。 – Darren 2012-04-21 06:53:33

+0

我同意'Async/ContinueWith'的东西使它感觉像嵌套娃娃。在我看来,更准确地说,就像MS那样,按照有序来描述它们。每个处理程序都被调用两次 - 一次在注册的顺序中,一次以相反顺序排出。下面的文章中的第三个图表明确了它。http://www.asp.net/web-api/overview/working-with-http/http-message-handlers – EBarr 2012-04-22 03:32:14

3

我在此基础上可在 Codeplex上的ASP.NET Web堆栈回购最新位在这里谈论。

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

比方说,我们有两个消息处理程序: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。

如果我们看一下框架内的引擎盖下了一点点,我们将看到HttpServer实例Initialize方法调用的System.Net.Http.HttpClientFactoryCreatePipeline方法(以前称为HttpPipelineFactory.Create方法,阿里表示。)CreatePipeline方法接受两个参数:HttpMessageHandlerIEnumerable<DelegatingHandler>HttpServer.Initialize方法传递System.Web.Http.Dispatcher.HttpControllerDispatcherHttpMessageHandler参数作为链和HttpConfiguration.MessageHandlers为IEnumerable<DelegatingHandler>参数内的最后HttpMessageHandler

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是最后一个消息处理程序在链中运行。

至于调用两次的问题,实际上并不是真的。另一方面,消息处理程序不会被调用两次,您将提供的继续方法将会是。这取决于你做到这一点。如果你提供了一个回调(换句话说,就是continuation),你的消息处理程序将在返回给客户端的路上被调用,并生成你可以使用的响应消息。

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

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内的第二个将成为第二个。