【问题标题】:ASP.NET Web API Message HandlersASP.NET Web API 消息处理程序
【发布时间】:2012-04-20 17:50:12
【问题描述】:

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我在这里谈论的是基于最新的位 Codeplex ASP.NET Web Stack 存储库。

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

      假设我们有两个消息处理程序: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 Ali 指出的方法。)CreatePipeline 方法接受两个参数:HttpMessageHandlerIEnumerable<DelegatingHandler>HttpServer.Initialize 方法将 System.Web.Http.Dispatcher.HttpControllerDispatcher 传递给 HttpMessageHandler 参数作为链内的最后一个 HttpMessageHandler ,并将 HttpConfiguration.MessageHandlers 传递给 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;
      }
      

      如您所见,消息处理程序的顺序颠倒了,Matryoshka doll 被创建,但这里要小心:确保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 中的 continuation 方法将是返回的第一个方法,MyMessageHandler 中的方法将是第二个方法。

      【讨论】:

        【解决方案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;
            }
        

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

        【讨论】:

        • 我就是这么想的。我认为它也不支持提供者的概念。类似于 MVC 中的 IFilterprovider 接口。
        • 我同意Async/ContinueWith 的东西让人感觉像是套娃。在我看来,将它们描述为有序的更准确,就像 MS 所做的那样。每个处理程序被调用两次——一次在进入的路上(按照注册的顺序),一次在退出的路上以相反的顺序。下篇第三张图说清楚了..asp.net/web-api/overview/working-with-http/…
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-26
        • 2021-04-14
        • 1970-01-01
        • 2012-05-13
        • 1970-01-01
        相关资源
        最近更新 更多