2012-08-17 11 views
9

basato sul mio last post Sono riuscito a ottenere il dosaggio in corso ... fino a un certo punto. Oltre alla registrazione del gestore specifico percorso Ho anche 2 gestori di delegawebapi batching e delega ai gestori

  1. autenticare l'utente
  2. accedendo

il gestore lotto passa attraverso i gestori di delega autenticare l'utente e la registrazione della richiesta. quando messagehandlerinvoker inizia a inviare le richieste child/nested viene lanciata la seguente eccezione.

System.ArgumentException was unhandled by user code 
    HResult=-2147024809 
    Message=The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'AuthenticationMessageHandler' is not null. 
Parameter name: handlers 
    Source=System.Net.Http.Formatting 
    ParamName=handlers 
    StackTrace: 
     at System.Net.Http.HttpClientFactory.CreatePipeline(HttpMessageHandler innerHandler, IEnumerable`1 handlers) 
     at System.Web.Http.HttpServer.Initialize() 
     at System.Web.Http.HttpServer.<EnsureInitialized>b__3() 
     at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) 
     at System.Threading.LazyInitializer.EnsureInitialized[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) 
     at System.Web.Http.HttpServer.EnsureInitialized() 
     at System.Web.Http.HttpServer.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
     at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
     at RoutingRequest.Service.Startup.BatchMessageHandler.<>c__DisplayClassd.<PrcoessRequest>b__b(Task`1 m) in C:\CEI\Clients\Footlocker.com\FL - Vendor Routing Portal\source\RoutingRequest.Service\Startup\BatchMessageHandler.cs:line 45 
     at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke() 
     at System.Threading.Tasks.Task.Execute() 
    InnerException: 

C'è una opzione di configurazione che mi manca o devo ignorare i gestori deleganti?

modifica ecco il mio gestore di autenticazione.

public class AuthenticationMessageHandler 
    : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     SetCurrentUser(request); 
     return base.SendAsync(request, cancellationToken); 
    } 

    private void SetCurrentUser(HttpRequestMessage request) 
    { 
     var values = new List<string>().AsEnumerable(); 
     if (request.Headers.TryGetValues("routingrequest-username", out values) == false) return; 

     var username = values.First(); 

     var user = Membership.GetUser(username, true); 
     if (user == null) 
     { 
      var message = string.Format("membership information for '{0}' could not be found.", username); 
      throw new HttpRequestException(message); 
     } 

     var roles = Roles.GetRolesForUser(username); 

     Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(user.UserName), roles); 
    } 
} 

in base alla risposta di Kiran un httpserver sottoclasse risolve un problema e ne introduce un altro. Il mio fornitore di ruoli sta ricevendo un'eccezione di riferimento null. esaminandolo adesso.

+0

potresti pubblicare il codice di gestione dell'autenticazione e il suo codice di inizializzazione? –

risposta

12

Questo post sul blog identifica correttamente il problema, ma c'è una soluzione più semplice se si sta configurando OWIN utilizzando una classe Startup o OwinStartup:

Modificare la chiamata di configurazione OWIN da UseWebApi(this IAppBuilder builder, HttpConfiguration configuration); a UseWebApi(this IAppBuilder builder, HttpServer httpServer); in modo che il gestore di un lotto e la pipeline OWIN utilizza la stessa istanza HttpServer.

La causa principale di ciò è che molti degli articoli dosaggio/Esempi (es http://bradwilson.typepad.com/blog/2012/06/batching-handler-for-web-api.html) creare un nuovo HttpServer per dosaggi in aggiunta alla principale HttpServer che gestisce le richieste HTTP; ed entrambi HttpServer usano lo stesso HttpConfiguration.

Quando ogni HttpServer viene inizializzata la prima volta che riceve richieste, si crea una pipeline di gestori (in HttpClientFactory.CreatePipeline) invertendo tutti i gestori delegando configurati (individuazione movimentatori o altri gestori proxy-type), e chiude la conduttura con il dispatcher dell'API Web.

Se non si dispone di gestori delegati configurati, questo problema non ti morderà: è possibile avere 2 oggetti HttpServer che utilizzano lo stesso HttpConfiguration.

Tuttavia, se si dispone di gestori delegati configurati in modo esplicito o implicito (ad esempio da enabling Web API Tracing), l'API Web non può creare la seconda pipeline - i gestori deleganti sono già collegati nella prima pipeline - e questa eccezione viene generata sul prima richiesta al 2o HttpServer.

Questa eccezione dovrebbe essere assolutamente più chiara su cosa sta succedendo. Meglio ancora, questo problema non dovrebbe essere nemmeno possibile: la configurazione dovrebbe essere la configurazione, non i singoli gestori.La configurazione potrebbe essere una fabbrica per la delega di gestori. Ma sto divagando ...

Se la questione è un pò difficile da capire, c'è una correzione piuttosto facile:

  1. Se stai usando OWIN, passare lo stesso HttpServer come si usa nel gestore lotto alla pipeline OWIN via UseWebApi(this IAppBuilder builder, HttpServer httpServer);
  2. Se si utilizza IIS + Web API (nessuna classe OWIN di avvio), passare GlobalConfiguration.DefaultServer al gestore di batch, per evitare di creare un nuovo HttpServer

Ecco un esempio OWIN classe di avvio che crea un singolo HttpServer e lo passa sia al gestore batch che all'API Web. Questo esempio viene utilizzato per gestore batch OData:

[assembly: OwinStartup(typeof(My.Web.OwinStartup))] 
namespace My.Web 
{ 

    /// <summary> 
    /// OWIN webapp configuration. 
    /// </summary> 
    public sealed class OwinStartup 
    { 

     /// <summary> 
     /// Configure all the OWIN modules that participate in each request. 
     /// </summary> 
     /// <param name="app">The OWIN appBuilder</param> 
     public void Configuration(IAppBuilder app) 
     { 
      HttpConfiguration webApiConfig = new HttpConfiguration(); 
      webApiConfig.MapHttpAttributeRoutes(); 

      HttpServer webApiServer = new HttpServer(webApiConfig); 

      // Configure batch handler 
      var batchHandler = new DefaultODataBatchHandler(webApiServer); 
      webApiConfig.Routes.MapODataServiceRoute("ODataRoute", 
                "odata", 
                BuildEdmModel(), 
                new DefaultODataPathHandler(), 
                ODataRoutingConventions.CreateDefault(), 
                batchHandler); 

      app.UseWebApi(webApiServer); 
     } 

     private EdmModel BuildEdmModel() 
     { 
      // ... 
     } 
    } 

} 
+1

interessante! Nella configurazione predefinita 'public void Configuration (IAppBuilder app)' di startup.cs (dove OWIN di default configura Cors & co), come posso ottenere l'httpServer? Grazie –

+1

Aggiunto un esempio alla mia risposta. – crimbo

+0

Chiamare l'inizializzazione 'IOC' dopo' UseWebApi', altrimenti si potrebbe ottenere un errore "Impossibile riutilizzare un'istanza 'ApiController'. 'ApiController' deve essere costruito per messaggio in arrivo. Controlla il tuo 'IHttpControllerActivator' personalizzato e assicurati che non produca il stessa istanza. ". Nel mio caso erano 'UseAutofacMiddleware' e' UseAutofacWebApi' –

1

Ho avuto questo errore senza batch. Ho creato uno HttpClientFactory di mia proprietà e contiene uno HandlerFactory, anch'esso il mio.

Chiama il metodo HandlerFactory.Create() nel costruttore e memorizza i gestori risultanti da esso creati.

Questi sono passati al metodo System.Net.Http.HttpClientFactory.Create(...) ogni volta che la fabbrica deve creare un nuovo HttpClient.

Ma è solo buono per una singola chiamata perché i gestori stessi sono mutati dal codice .NET lasciandoli in uno stato che significa che non possono essere riutilizzati.

Ho modificato il mio costruttore in modo che non crei i gestori davanti, ma ogni volta. Ora funziona.