2012-04-20 2 views
5

Возможно ли контролировать порядок выполнения пользовательских обработчиков сообщений?Обработчики сообщений ASP.NET Web API

В качестве примера, я бы хотел, чтобы обработчик ведения журнала выполнялся первым, поэтому я всегда регистрирую запрос.

Помимо добавления последнего обработчика журналов, я не вижу, как это сделать.

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

ответ

7

Порядок, в котором вы регистрируете обработчики, определяет, когда они вызывается, но, как указывает Aliostad, они работают в модели русской куклы, поэтому первая из них также называется последней и так далее.

Зарегистрированные handlesr вызываются снизу вверх по входящему пути и сверху вниз в исходящих. То есть, последняя запись сначала вызывается для сообщения входящего запроса, но вызывается последним для сообщения исходящего ответа.

3

No - 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

Вот что я думал. Я не думаю, что он поддерживает концепцию поставщиков. Как интерфейс IFilterprovider в MVC. – Darren

+0

Я согласен с тем, что материал «Async/ContinueWith» заставляет его чувствовать себя гнездившимися куклами. На мой взгляд, более точно, чтобы описать их как упорядоченное, как это делает MS. Каждый обработчик вызывается дважды - один раз на пути в (в порядке зарегистрированного) и один раз выход в обратном порядке. Третья диаграмма в следующей статье дает понять. Http://www.asp.net/web-api/overview/working-with-http/http-message-handlers – EBarr

3

Я говорю здесь, основываясь на последних битах, которые доступны на Codeplex ASP.NET Web Stack repo.

Порядок заказанных пользователем приказов к каталогу. Позвольте мне объяснить:

Предположим, у нас есть два обработчика сообщений: 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 в качестве второго, другими словами ФИФО.

Если мы посмотрим на немного под капотом внутри рамки, то мы увидим, что Initialize метод экземпляра HttpServer является вызовом CreatePipeline метод System.Net.Http.HttpClientFactory (который был ранее известный как HttpPipelineFactory.Create способом, как указано Али.) CreatePipeline метод принимает два параметра: HttpMessageHandler и IEnumerable<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 будет первым, который будет вызываться на обратном пути, а второй внутри MyMessageHandler будет вторым.

Смежные вопросы