based on my last post I was able to get batching working... until a certain point. In addition to registering the route specific handler I also have 2 delegating handlers
the batch handler goes through the delegating handlers authenticating the user and logging the request. when the messagehandlerinvoker starts to send the child/nested requests the following exception is thrown.
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:
is there a config option I am missing, or do I need to bypass the delegating handlers?
edit here is my authentication handler.
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);
}
}
based on Kiran's answer a subclassed httpserver fixes one issue and introduces another. My roles provider is getting a null reference exception. looking into that now.
That blog post correctly identifies the problem, but there is a simpler solution if you are configuring OWIN using a Startup
or OwinStartup
class:
Change the OWIN configuration call from
UseWebApi(this IAppBuilder builder, HttpConfiguration configuration);
to
UseWebApi(this IAppBuilder builder, HttpServer httpServer);
so that your batch handler and the OWIN pipeline are using the same HttpServer
instance.
The root cause of this is that many of the batching articles/examples (eg http://bradwilson.typepad.com/blog/2012/06/batching-handler-for-web-api.html ) create a new HttpServer
for batching in addition to the main HttpServer
that is handling HTTP requests; and both HttpServer
s are using the same HttpConfiguration
.
When each HttpServer
is initialized the first time it receives requests, it creates a pipeline of handlers (in HttpClientFactory.CreatePipeline
) by reversing all the configured delegating handlers (eg tracing handlers, or other proxy-type handlers), and terminating the pipeline with the Web API dispatcher.
If you don't have any delegating handlers configured, then this problem won't bite you - you can have 2 HttpServer
objects that use the same HttpConfiguration
.
But if you have any delegating handlers explicitly or implicitly configured (eg by enabling Web API Tracing), then Web API can't build the 2nd pipeline - the delegating handlers are already linked in the first pipeline - and this exception is thrown on the first request to the 2nd HttpServer
.
This exception should absolutely be more clear about what is going on. Better yet, this problem shouldn't even be possible - configuration should be configuration, not individual handlers. The configuration could be a factory for delegating handlers. But I digress...
While the issue is kinda hard to figure out, there's a pretty easy fix:
HttpServer
as you use in the batch handler to the OWIN pipeline via UseWebApi(this IAppBuilder builder, HttpServer httpServer);
GlobalConfiguration.DefaultServer
to your batch handler, to avoid creating a new HttpServer
Here's an example OWIN startup class that creates a single HttpServer
and passes it to both the batch handler, and Web API. This example uses to OData batch handler:
[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()
{
// ...
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With