Introducing batch support in Web API and Web API OData

With the release of Microsoft ASP.NET Web API 2 OData, we have introduced support for batching requests. Batching is a web API feature that allows a customer to pack several API requests and send them to the web API service in one HTTP request and receive a single HTTP response with the response to all their requests. This way, the client can optimize calls to the server and improve the scalability of its service.

For a more in depth look at the batch support, you can take a look at the specification.

Batch in Web API

In order to start using batch in Web API, the only requirement is to register a route with a batch handler. Let’s start by creating the server. For this sample we will be using the brand new OWIN host for Web API. The code below creates the server and configures a web API route and a batch route.

The DefaultHttpBatchHandler requires an instance of an HttpServer to work. We can create this instance ourselves in the self-host scenario or we can get it by accessing it from GlobalConfiguration.DefaultServer.

The DefaultHttpBatchHandler executes each request sequentially, but in case you don’t have any dependency between requests, you can set a property to make execution non-sequential.

The MapHttpBatchRoute route template can contain parameters. So, in case you create your own batch handler, you can pass in parameters to it.

That’s all that is required to enable batch in your service. Let’s see how to send batch requests and read batch responses using HttpClient. In order to do that, we are going to need a model, a web API controller and a route to dispatch the incoming requests. We will be using Entity Framework as the backend and AutoFixture to generate some sample data for the service. Here is the code for all of it:

Now that we have a running service with some data on it, and we have enabled batching support, we can move on to actually perform batch requests. The default Web API batch implementation is based on the mime/multipart content type. It accepts mime/multipart requests containing multiple HTTP Requests and processes all the requests sending a mime/multipart response containing the individual responses. Let’s start by creating some requests:

The first block before the blank line only performs a query to get some customers that we can update and delete in a request later. At this point, we could just send four requests using HttpClient, and our service would just process the requests and send individual responses back.

In order to batch those requests together into a single HTTP request, we need to encapsulate them into HttpMessageContent instances and add those instances to a MultipartContent instance. Here is the code to do that:

If you look at the code above, the multipart content needs to have a subtype of mixed, and a boundary which is a unique identifier that determines the separation between the different parts of the content.

Now that we have created the multipart/mixed content, the only last thing that we need to do in order to have a valid batch request is create the HTTP request and associate the content to it. We can do that as we see in the following fragment:

//Create the request to the batch service
HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345/api/batch");
//Associate the content with the message
batchRequest.Content = content;

With this last part, we are ready to send a batch request to the server and get a response. To do that, we just use an HttpClient instance and call SendAsync passing the request message to it and capture the associated response. If we just do that, the following request gets send and the following response comes back:

If we look at the request above, we can see, as we have said before, that all the messages are separated with a boundary, which in this case is “batch_357647d1-a6b5-4e6a-aa73-edfc88d8866e”. Also, we can see that every request message has a Content-Type of application/http. This header is introduced by the HttpContent where we wrapped all our requests. If we wanted to, we could have added extra headers that we could read and use for handling the processing of the message in a custom batch handler.

If we look at the response below we can clearly see that it follows the same pattern, with a multipart/mixed content type, a boundary to separate the different parts of the multipart and a collection of responses encapsulated in HttpContent parts.

Finally, now that we have sent and received a response we need to read it and extract all the individual responses from the content. For accomplishing this task, we can use the ReadAsMimeMultipartAsync and ReadAsHttpResponseMessageAsync methods on the Content property of the response.

And with this, we have successfully sent and read a batch request to Web API. Let’s look at how to do the same thing with Web API OData.

Batch in OData

In order to use batch with OData services, the flow is very similar to the flow in Web API. We will be reusing the model and the backend from the above sample, and the only things we’ll need are an OData model a route that defines the OData endpoint and a controller to handle the incoming requests. All of which can be shown below:

As you can see from the fragment above, MapODataRoute accepts an ODataBatchHandler as a parameter. In this case, we are using the DefaultODataBatchHandler, but we could also use the UnbufferedODataBatchHandler.

We could use HttpClient to send batch requests to our OData service, but it’s much easier if we make use of the generated client built in through the Add Service Reference dialog in Visual Studio 2013.

To do that, we can just start the service by pressing Ctrl+F5, then we only need to go to the References icon of the project Right Click, Add Service Reference and give it the metadata url of our service which in this case is http://localhost:12345/odata/$metadata.

Now that we have a generated client, we can just start using it to work with the OData endpoint and batch requests. The generated client supports two flows. It can batch a set of queries and return a batch response with the responses to the individual queries, or it can batch a change set of requests that perform modifications on the server.

Finally, the wire format for OData batch requests is the same as the format for Web API batch requests but with a few minor differences. In OData, requests in a batch are divided in two categories. query operations and change sets. query operations, as it name implies, don’t perform any modification on the server, in contrast with change sets, that basically group a set of state changing operations as a unit.

These concepts get reflected on the format of the request and of the response. Basically change sets get encoded as nested mime/multipart parts of the external mime/multipart content that is the batch request. The response follows the same structure, as we can see in the response below:

I'm curious what the rationale for using multipart application/http messages is, rather than using persistent connections and pipelining while presenting the multiple requests server-side as a batch. Could you go into this?

Hello,
We have noticed that when we execute this call using POSTMAN, we get the following error: Unexpected end of MIME multipart stream. MIME multipart message is not complete.

It appears that this is due to the fact that POSTMAN doesn’t append a newline to the multi-part message. There have been numerous solutions proposed on Stack overflow, but none of those seem to be working. Can you please confirm if this is a known bug in the .NET library? Is there an ETA on when this can be fixed?

Hello,
My batch is failing after inserting records and it does not clear added records from proxy state, its still Added, so when it goes next time to execute something for batch again , it inserts those records again since it failed last time after inserting. The error was in some records for foreign key. I want to know hoe to solve this issue, so that it does not add already added records into database.
Thanks,
Manish