Overview
Request batching is a useful way of minimizing the number of messages that are passed between the client and the server. This reduces network traffic and provides a smoother, less chatty user interface. This feature will enable Web API users to submit multiple service calls in a single HTTP request.
Design
BatchHandler
This is a custom HttpMessageHandler that will be used to handle the batch requests.The BatchHandler takes two arguments in the constructor: an HttpServer and an IBatchProcessor.
- HttpServer will be used to process the individual batch requests.
- IBatchProcessor will be used to parse the request into individual batch requests and submit them to the HttpServer.
namespace System.Web.Http.Batch { publicclass BatchHandler : HttpMessageHandler { public BatchHandler(HttpServer httpServer, IBatchProcessor batchProcessor); public IBatchProcessor BatchProcessor { get; } protectedoverride Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); } }
IBatchProcessor
This is an abstraction for processing the batch requests.
namespace System.Web.Http.Batch { publicinterface IBatchProcessor { Task<HttpResponseMessage> ExecuteAsync(HttpRequestMessage request, HttpMessageInvoker invoker, CancellationToken cancellationToken); } }
An implementation of IBatchProcessor will do the following:
- Parse the incoming request into batch requests
- Execute the batch requests
- Build the batch response
The idea is to have different IBatchProcessor implementations that understand different batch request/response formats and know how to process them.
Out of the box, we’re providing the DefaultBatchProcessor for Web API Batching
namespace System.Web.Http.Batch { publicclass DefaultBatchProcessor : IBatchProcessor { publicbool OrderedExecution { get; set; } publicvirtual HttpResponseMessage CreateResponseMessage(IList<HttpResponseMessage> responses, HttpRequestMessage request); publicvirtual Task<HttpResponseMessage> ExecuteAsync(HttpRequestMessage request, HttpMessageInvoker invoker, CancellationToken cancellationToken); publicvirtual Task<IList<HttpResponseMessage>> ExecuteRequestMessagesAsync(IEnumerable<HttpRequestMessage> requests, HttpMessageInvoker invoker, CancellationToken cancellationToken); publicvirtual Task<IList<HttpRequestMessage>> ParseBatchRequests(HttpRequestMessage request); protectedvirtualvoid ValidateRequest(HttpRequestMessage request); } }
datajs client
OData.request({ requestUri: "/odata/$batch", method: "POST", data: { __batchRequests: [ { __changeRequests: [ { requestUri: "/odata/Customers", method: "POST", data: customer } ] }, { requestUri: "/odata/Customers", method: "GET" } ] } }, function (data, response) { //success handler }, function () { alert("request failed"); }, OData.batchHandler);
Scenarios
Web API Batching
Registering default batch endpoint
You can use MapBatchRoute, which is an HttpRouteCollection extension method, to create a batch endpoint.
config.Routes.MapBatchRoute("apiBatch", "api/batch", GlobalConfiguration.DefaultServer);
Under the hood it just uses MapHttpRoute.
config.Routes.MapHttpRoute("apiBatch", "api/batch", null, null, new BatchHandler(GlobalConfiguration.DefaultServer, new DefaultBatchProcessor()));
Ordered Execution
By default each individual batch request is executed asynchronously. Meaning there’s no guarantee that the first batch request will finish executing before kicking off the second one. So if you have a scenario where you want to get the results after all the posts, you can enable the OrderedExecution which will process the requests in order.
var batchProcessor = new DefaultBatchProcessor();
batchProcessor.OrderedExecution = true;
config.Routes.MapBatchRoute("apiBatch", "api/batch", new BatchHandler(GlobalConfiguration.DefaultServer, batchProcessor));
OData Batching
Registering OData batch endpoint
You can use MapODataBatchRoute, which is an HttpRouteCollection extension method, to create an OData batch endpoint.
config.Routes.MapODataBatchRoute("odataBatch", "odata/$batch", GlobalConfiguration.DefaultServer);
Under the hood it just uses MapHttpRoute.
config.Routes.MapHttpRoute("odataBatch", "odata/$batch", null, null, new BatchHandler(GlobalConfiguration.DefaultServer, new ODataBatchProcessor()));
Configuring Batch Quotas
Work in progress…
var odataBatchProcessor = new ODataBatchProcessor();
odataBatchProcessor.MessageQuotas.MaxOperationsPerChangeset = 10;
odataBatchProcessor.MessageQuotas.MaxPartsPerBatch = 50;
config.Routes.MapBatchRoute("odataBatch", "odata/$batch", new BatchHandler(GlobalConfiguration.DefaultServer, odataBatchProcessor));
Order of Execution
According to the OData spec, the operation/ChangeSet within a batch request is executed in ordered manner. Operations within the ChangeSet can be executed regardless of the order but our implementation will execute them in order for simplicity.
Transaction
Work in progress…
Custom Batching
Work in progress…