2012-04-20 15 views
5

¿Es posible controlar el orden de ejecución de los manejadores de mensajes personalizados?Gestores de mensajes de la API web ASP.NET

Como ejemplo, es posible que desee que se ejecute primero un controlador de inicio de sesión, por lo que siempre registro una solicitud.

Además de agregar el controlador de inicio de sesión por última vez, no estoy viendo cómo lograrlo.

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

Respuesta

7

El orden en el que registra los manejadores determina cuándo se llaman pero, como Aliostad señala, funcionan en un modelo de muñeca rusa, por lo que el primero que entra también se llama como el último y así sucesivamente.

El handlesr registrado se invoca de forma ascendente en la ruta entrante y de arriba hacia abajo en la salida. Es decir, la última entrada se llama primero para un mensaje de solicitud entrante pero se invoca por último para un mensaje de respuesta saliente.

3

No - AFAIK.

Es el modelo de Muñeca Rusa, con un manejador dentro de otro hasta que el último haga el trabajo. Esto se construye en la clase interna HttpPipelineFactory (se puede ver el código fuente como fue liberado):

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

Así lo hace es el código para obtener una lista y luego convertirlo en un muñeca rusa.

+0

Eso es lo que pensé. No creo que sea compatible con el concepto de proveedores tampoco. Al igual que la interfaz IFilterprovider en MVC. – Darren

+0

Acepto que las cosas 'Async/ContinueWith' hacen que parezca muñecas de anidación. Es más preciso, en mi opinión, describirlos como ordenados, como lo hace MS. Cada controlador se llama dos veces: una vez en el camino hacia adentro (en el orden registrado) y una vez que sale en orden inverso. El tercer diagrama en el siguiente artículo lo deja claro .. http://www.asp.net/web-api/overview/working-with-http/http-message-handlers – EBarr

3

Estoy hablando aquí basado en los últimos bits que están disponibles en Codeplex ASP.NET Web Stack repo.

La orden es controlada por el usuario y no hay ningún orden arbitrario aquí. Permítanme explicar:

Digamos que tenemos dos manejadores de mensajes: MyMessageHandler y MyMessageHandler2. Suponiendo que se registren como a continuación:

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

Lo que espera aquí es para el MyMessageHandler ejecutar primero y MyMessageHandler2 como segunda, en otras palabras FIFO.

Si miramos un poco debajo de la capilla en el interior del marco, veremos que Initialize método de la HttpServer ejemplo invoca CreatePipeline método de System.Net.Http.HttpClientFactory (que se conocía anteriormente como HttpPipelineFactory.Create método de los indicados Ali.) CreatePipeline método acepta dos parámetros: HttpMessageHandler y IEnumerable<DelegatingHandler>. El método HttpServer.Initialize está pasando System.Web.Http.Dispatcher.HttpControllerDispatcher para el parámetro HttpMessageHandler como el último HttpMessageHandler dentro de la cadena y HttpConfiguration.MessageHandlers para el parámetro IEnumerable<DelegatingHandler>.

¿Qué pasa dentro del método CreatePipeline es muy inteligente 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; 
} 

Como se puede ver, el orden controlador de mensajes se invierte y el Matryoshka doll se crea pero tenga cuidado aquí: se asegura que HttpControllerDispatcher es el último controlador de mensajes para ejecutar dentro de la cadena.

En cuanto a la llamada dos veces, en realidad no es del todo cierto. El manejador de mensajes no se llamará dos veces, el método de continuación que proporcionará va a ser, por otro lado. Depende de usted hacer que suceda. Si proporciona una devolución de llamada (en otras palabras, continuación), se llamará a sus manejadores de mensajes en el camino de regreso al cliente con el mensaje de respuesta generado con el que puede jugar.

Por ejemplo, vamos a suponer que los dos siguientes son los controladores de mensajes que hemos registrados anteriormente:

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

Y este es el otro:

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

Como hemos proporcionado continuación, nuestra los manejadores de mensajes serán llamados nuevamente en el camino de regreso al cliente en orden FILO. Por lo tanto, el método de continuación dentro del MyMessageHandler2 será el primero invocado en el camino de regreso y el que se encuentra dentro del MyMessageHandler será el segundo.