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
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()
{
// ...
}
}
}
I've had this error without batching. I made an HttpClientFactory
of my own and it takes in a HandlerFactory
, also my own.
It calls the HandlerFactory.Create()
method in the constructor and stores the resulting handlers that it made.
These are passed to the System.Net.Http.HttpClientFactory.Create(...)
method whenever the factory needs to make a new HttpClient
.
But it's then only good for a single call because the handlers themselves are mutated by the .NET code leaving them in a state that means they cannot be reused.
I altered my constructor so that it doesn't create the handlers up front, but each time. It now works.