Cómo añadir con WebApi un enpoint batch

jueves, 2 de octubre de 2014


Desde ASP.NET Web Api 2 tenemos la posibilidad de crear un endpoint batch en los servicios http que creamos. Esta característica nos da la posibilidad de agrupar en una sola petición http desde cliente múltiples peticiones http, de esta forma se reduce el tráfico desde cliente a servidor.


Vamos a ver como podemos habilitar esta característica y un ejemplo de uso.

Registrar batch enpoint

Para registrar este endpoint, nos vamos a la clase WebApiConfig que nos crea por defecto la plantilla de proyecto Web Api y añadimos una nueva ruta.

config.Routes.MapHttpBatchRoute(
    routeName: "WebApiBatch", 
    routeTemplate: "api/batch",
    batchHandler: new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer));


Fijaros que asignamos como BatchHandler un DefaultHttpBatchHandler, este handler será el que se encargará de recibir la petición http agrupada y trata cada petición de forma individual pero ya en servidor.

Podemos configurar este handler para que trate de forma secuencial o no las peticiones agrupadas. Tenemos que crearnos antes de registrar la ruta batch el handler y le asignamos la propiedad ExecutionOrder, donde le podemos indicar BatchExecutionOrder.NonSequential o BatchExecutionOrder.Sequential, siendo este último el valor por defecto.

    HttpBatchHandler batchHandler = new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer)
    {
        ExecutionOrder = BatchExecutionOrder.Sequential
    };

    config.Routes.MapHttpBatchRoute(
                    routeName: "WebApiBatch",
                    routeTemplate: "api/batch",
                    batchHandler: batchHandler);


Para el que tenga curiosidad, podéis ver el código de DefaultHttpBatchHandler aquí.

Tenemos la opción de crearnos nuestro propio handler batch personalizado heredando de este y sobeescribir el método encargado de tratar cada petición o crear por ejemplo nuestros propios mensajes de respuesta para cada petición.

Ejemplo

Lo primero es registrar el endpoint batch como hemos visto antes.

Creamos un controlador de usuarios con un método post para dar del alta usuarios.
public class UsersController : ApiController
{
    public HttpResponseMessage Post([FromBody]string userName)
    {
        Debug.Write(string.Format("User {0} created",userName));

        return Request.CreateResponse(HttpStatusCode.OK);
    }
}


Ahora ya podemos desde cliente atacar al endpoint batch creandonos un HttpRequestMessage donde el contenido va a ser un MultipartContent que va a contener cada petición individual a realizar al servidor

string baseAddress = "http://localhost:7164";
HttpClient client = new HttpClient();
HttpRequestMessage batchRequest = new HttpRequestMessage(HttpMethod.Post, baseAddress + "/api/batch")
{
    Content = new MultipartContent("mixed")
    {
        new HttpMessageContent(new HttpRequestMessage(HttpMethod.Post, baseAddress + "/api/users")
        {
            Content = new ObjectContent("Arya Stark", new JsonMediaTypeFormatter())
        }),

        new HttpMessageContent(new HttpRequestMessage(HttpMethod.Post, baseAddress + "/api/users")
        {
            Content = new ObjectContent("Daenerys Targaryen", new JsonMediaTypeFormatter())
        }),
    }
};

HttpResponseMessage batchResponse = client.SendAsync(batchRequest).Result;

MultipartStreamProvider streamProvider = batchResponse.Content.ReadAsMultipartAsync().Result;
foreach (var content in streamProvider.Contents)
{
    HttpResponseMessage response = content.ReadAsHttpResponseMessageAsync().Result;

    Debug.WriteLine(response.StatusCode);
}


Cuando nos llega la respuesta vamos a poder obtener la respuesta de cada petición que habiamos incluido en la petición batch.

Formatos raw

A continuación vamos a ver el formato raw de los mensajes entre servidor y cliente para el ejemplo que hemos visto.

Formato de la petición

POST http://localhost:7164/api/batch HTTP/1.1
Content-Type: multipart/mixed; boundary="91731eeb-d443-4aa6-9816-560a8aca66b1"
Connection: Keep-Alive
Host: localhost:7164
Content-Length: 434

--91731eeb-d443-4aa6-9816-560a8aca66b1
Content-Type: application/http; msgtype=request

POST /api/users HTTP/1.1
Host: localhost:7164
Content-Type: application/json; charset=utf-8

"Arya Stark"
--91731eeb-d443-4aa6-9816-560a8aca66b1
Content-Type: application/http; msgtype=request

POST /api/users HTTP/1.1
Host: localhost:7164
Content-Type: application/json; charset=utf-8

"Daenerys Targaryen"
--91731eeb-d443-4aa6-9816-560a8aca66b1--    

Formato de la respuesta

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 268
Content-Type: multipart/mixed; boundary="bbee1acb-1bb0-4b3c-8c0c-8744672bb8a7"
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNceHVyeG9cRHJvcGJveFxEZXNhcnJvbGxvXFByb3llY3Rvc1BlcnNvbmFsZXNcWHVyeG9EZXZlbG9wZXIuQmxvZ3Nwb3RcV2ViQXBpQmF0Y2hcRXhhbXBsZVxXZWJBcGlCYXRjaEV4YW1wbGVcV2ViQXBpQmF0Y2hFeGFtcGxlXGFwaVxiYXRjaA==?=
X-Powered-By: ASP.NET
Date: Tue, 30 Sep 2014 21:04:35 GMT

--bbee1acb-1bb0-4b3c-8c0c-8744672bb8a7
Content-Type: application/http; msgtype=response

HTTP/1.1 200 OK


--bbee1acb-1bb0-4b3c-8c0c-8744672bb8a7
Content-Type: application/http; msgtype=response

HTTP/1.1 200 OK


--bbee1acb-1bb0-4b3c-8c0c-8744672bb8a7--

Resumen

Hemos visto como poder añadir un endpoint batch a un servicio http usando Web Api. A nivel de servidor solo hay que registar la ruta batch porque el DefaultHttpBatchHandler se va a encargar de tratar esa petición batch ejecutando cada petición que trae en el contenido de forma individual.
En cliente tenemos que crearnos una petición y en su contenido agrupar cada petición individual que queremos hacer al servidor.

Libros relacionados

ASP.NET Web API 2: Building a REST Service from Start to Finish

Designing Evolvable Web APIs with ASP.NET

ASP.NET Web API 2 Recipes: A Problem-Solution Approach

No hay comentarios:

Publicar un comentario