2012-04-20 19 views
5

È possibile controllare l'ordine di esecuzione dei gestori di messaggi personalizzati?Gestori di messaggi API Web ASP.NET

Ad esempio, potrei voler eseguire prima un gestore di registrazione, quindi registro sempre una richiesta.

Oltre ad aggiungere l'ultimo gestore di registrazione, non riesco a vedere come ottenere ciò.

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

risposta

7

L'ordine in cui si registrano i gestori determina quando vengono chiamati, ma come Aliostad indica che lavorano in un modello di bambola russa, quindi il primo in viene anche chiamato come l'ultimo fuori e così via.

Gli handler registrati vengono richiamati in modo bottom-up nel percorso in entrata e dall'alto in basso in uscita. Cioè, l'ultima voce viene chiamata prima per un messaggio di richiesta in arrivo ma invocata per ultima per un messaggio di risposta in uscita.

3

No - AFAIK.

È il modello di bambola russa, con un gestore all'interno di un altro fino a quando l'ultimo a fare il lavoro. Questo è costruito in classe interna HttpPipelineFactory (è possibile visualizzare il codice sorgente è stato rilasciato):

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

Così che cosa fa il codice è quello di ottenere una lista e poi trasformarlo in una bambola russa.

+0

Questo è quello che pensavo. Non penso nemmeno che supporti il ​​concetto di provider. Come l'interfaccia IFilterprovider in MVC. – Darren

+0

Sono d'accordo che il materiale 'Async/ContinueWith' lo fa sentire come un nido di bambole. È più accurato, secondo me, descriverli come ordinati, come fa MS. Ogni gestore viene chiamato due volte - una volta in entrata (nell'ordine registrato) e una volta in uscita in ordine inverso. Il terzo diagramma nel seguente articolo chiarisce ... http://www.asp.net/web-api/overview/working-with-http/http-message-handlers – EBarr

3

sto parlando qui in base alle punte più recenti che sono disponibili su Codeplex ASP.NET Web Stack pronti contro termine.

L'ordine è controllato dall'utente e non vi è alcun ordine arbitrario qui. Lasciatemi spiegare:

Diciamo che abbiamo due gestori di messaggi: MyMessageHandler e MyMessageHandler2. Partendo dal presupposto che noi li registriamo come di seguito:

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

Cosa vi aspettate qui è per la MyMessageHandler a correre prima e MyMessageHandler2 come secondo, in altre parole FIFO.

Se guardiamo un po 'sotto il cofano all'interno del quadro, vedremo che Initialize metodo dell'istanza HttpServer invoca CreatePipeline metodo System.Net.Http.HttpClientFactory (che in precedenza era conosciuto come HttpPipelineFactory.Create Metodo Ali indicato.) CreatePipeline metodo accetta due parametri: HttpMessageHandler e IEnumerable<DelegatingHandler>. Il metodo HttpServer.Initialize sta passando System.Web.Http.Dispatcher.HttpControllerDispatcher per il parametro HttpMessageHandler come ultimo HttpMessageHandler all'interno della catena e HttpConfiguration.MessageHandlers per il parametro IEnumerable<DelegatingHandler>.

Cosa accade all'interno del metodo CreatePipeline è molto intelligente 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; 
} 

Come si può vedere, l'ordine gestore di messaggi è invertito e il Matryoshka doll è creato ma attenzione qui: è garantito che HttpControllerDispatcher è la l'ultimo gestore di messaggi da eseguire all'interno della catena.

Per quanto riguarda il doppio problema di chiamata, in realtà non è del tutto vero. Il gestore di messaggi non verrà chiamato due volte, il metodo di continuazione che fornirai sarà, d'altra parte. Spetta a te farlo accadere. Se fornisci un callback (in altre parole continuazione), i gestori dei messaggi verranno richiamati al cliente con il messaggio di risposta generato con cui puoi giocare.

Per esempio, supponiamo che il seguente due sono i gestori di messaggi che abbiamo registrato in precedenza:

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

E questa è l'altra:

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

Come abbiamo fornito la continuazione, la nostra i gestori dei messaggi saranno richiamati sulla via del ritorno al cliente in ordine FILO. Quindi, il metodo di continuazione all'interno di MyMessageHandler2 sarà il primo a essere richiamato al ritorno e quello all'interno di MyMessageHandler sarà il secondo.