【ASP.NET Web API教程】5.4 ASP.NET Web API批處理器


注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本系列教程,請先看前面的內容。

Batching Handler for ASP.NET Web API
5.4 ASP.NET Web API批處理器

本文引自:http://bradwilson.typepad.com/blog/2012/06/batching-handler-for-web-api.html

Brad Wilson | June 20, 2012
作者:Brad Wilson | 日期:2012-6-20

While there is no batching standard built into the HTTP protocol, there is a standard for MIME encoding HTTP request and response messages ("application/http" with "msgtype=request" and "msgtype=response", respectively). ASP.NET Web API has built-in support for both MIME multipart as well as encoded request and response messages, so we have all the building blocks we need to make a simple batch request handler.
當批處理標准尚未進入HTTP協議時,就已經有了對HTTP請求和響應消息進行編碼的MIME標准(分別采用“msgtype=request”和“msgtype=response”的“application/http”)。ASP.NET Web API對MIME的multipart(多部分內容類型)、以及經過編碼請求和響應消息都有內建的支持,因此,我們擁有了制作簡單的請求批處理器的全部構建塊。

All we need to make this work is an endpoint which can accept a multipart batch (an invention of our own), which then parses the requests, runs them sequentially, and returns the responses back in a multipart batch response.
我們所要做的全部工作只是一個端點(endpoint),它可以接收一個multipart batch(多部批,一個我們自己發明的內容類型),然后用它對請求進行解析,按順序執行請求,並以一個multipart batch響應的形式返回一個響應。

Starting with a Web API project (built against the latest nightly build), I updated the Web API config to look like this:
從一個Web API項目(根據最新版建立的項目)開始,我修改了Web API的config,它看上去像這樣:

var batchHandler = new BatchHandler(config);
config.Routes.MapHttpRoute("batch", "api/batch", null, null, batchHandler);
config.Routes.MapHttpRoute("default", "api/{controller}/{id}", new { id = RouteParameter.Optional });

I've inserted the handler for "api/batch" as our endpoint for batching requests, using the new "route-specific endpoint handler" feature in Web API. Note that since its URL is "api/batch", I made sure to add it before the default API route.
我已經為“api/batch”插入了處理器,以此作為對請求進行批處理的端點,這種做法利用了Web API中的“路由專用的端點處理器”特性。注,由於它的URL是“api/batch”,必須把它添加在默認的API路由之前

Using async & await in .NET 4.5 makes the implementation of BatchHandler fairly straight-forward. All we need is an in-memory HttpServer which uses our existing configuration, so that the batched requests hit the exact same endpoints as requests from the Internet:
利用.NET 4.5中的async和await可以很直接地構造BatchHandler實現。我們所需要的只是一個放在內存中的HttpServer,它使用當前配置,以便當請求來自Internet時,需要分批的請求會找到完全相同的端點:

public class BatchHandler : HttpMessageHandler
{
    HttpMessageInvoker _server; 
public BatchHandler(HttpConfiguration config) { _server = new HttpMessageInvoker(new HttpServer(config)); }
protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { // Return 400 for the wrong MIME type // 對於錯誤的MIME類型,返回400 if ("multipart/batch" != request.Content.Headers.ContentType.MediaType) { return request.CreateResponse(HttpStatusCode.BadRequest); }
// Start a multipart response // 啟動一個multipart響應 var outerContent = new MultipartContent("batch"); var outerResp = request.CreateResponse(); outerResp.Content = outerContent;
// Read the multipart request // 讀取multipart請求 var multipart = await request.Content.ReadAsMultipartAsync(); foreach (var httpContent in multipart.Contents) { HttpResponseMessage innerResp = null; try { // Decode the request object // 解碼請求對象 var innerReq = await httpContent.ReadAsHttpRequestMessageAsync();
// Send the request through the pipeline // 通過管線發送請求 innerResp = await _server.SendAsync( innerReq, cancellationToken ); } catch (Exception) { // If exceptions are thrown, send back generic 400 // 如果拋出異常,回發泛型的400 innerResp = new HttpResponseMessage( HttpStatusCode.BadRequest ); }
// Wrap the response in a message content and put it // into the multipart response // 在消息內容中封裝響應,並把它放入multipart響應 outerContent.Add(new HttpMessageContent(innerResp)); }
return outerResp; } }

Now we have an endpoint that we can send multipart/batch requests to, which are assumed to be HTTP request objects (anything which isn't is going to yield a 400).
現在,我們擁有了一個端點,我們能夠把multipart/batch請求發送給它,假設這些請求都是HTTP請求對象(任何不是HTTP請求的對象都會產生一個400狀態碼)。

On the client side, we make a multipart request and push requests into the multipart batch, one at a time:
在客戶端,我們形成了一個multipart請求,並把請求推入multipart batch,每次壓入一個請求:

var client = new HttpClient();
var batchRequest = new HttpRequestMessage(
    HttpMethod.Post,
    "http://localhost/api/batch"
); 
var batchContent = new MultipartContent("batch"); batchRequest.Content = batchContent;
batchContent.Add( new HttpMessageContent( new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/values" ) ) );
batchContent.Add( new HttpMessageContent( new HttpRequestMessage( HttpMethod.Get, "http://localhost/foo/bar" ) ) );
batchContent.Add( new HttpMessageContent( new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/values/1" ) ) );

In a console application, we can log both the request and response with code like this:
在一個控制台應用程序中,我們可以用以下代碼對請求和響應時行日志:

using (Stream stdout = Console.OpenStandardOutput())
{
    Console.WriteLine("<<< REQUEST >>>");
    Console.WriteLine();
    Console.WriteLine(batchRequest);
    Console.WriteLine();
batchContent.CopyToAsync(stdout).Wait();
Console.WriteLine(); var batchResponse = client.SendAsync(batchRequest).Result; Console.WriteLine("<<< RESPONSE >>>"); Console.WriteLine(); Console.WriteLine(batchResponse); Console.WriteLine(); batchResponse.Content.CopyToAsync(stdout).Wait(); Console.WriteLine(); Console.WriteLine(); }

When I run this console application, I see output similar to this:
當運行這個控制台應用程序時,會看到輸出類似於這樣:

<<< REQUEST >>> 
Method: POST, RequestUri: 'http://localhost/api/batch', Version: 1.1, Content: System.Net.Http.MultipartContent, Headers: { Content-Type: multipart/batch; boundary="3bc5bd67-3517-4cd0-bcdd-9d23f3850402" }
--3bc5bd67-3517-4cd0-bcdd-9d23f3850402 Content-Type: application/http; msgtype=request
GET /api/values HTTP/1.1 Host: localhost
--3bc5bd67-3517-4cd0-bcdd-9d23f3850402 Content-Type: application/http; msgtype=request GET /foo/bar HTTP/1.1 Host: localhost
--3bc5bd67-3517-4cd0-bcdd-9d23f3850402 Content-Type: application/http; msgtype=request
GET /api/values/1 HTTP/1.1 Host: localhost
--3bc5bd67-3517-4cd0-bcdd-9d23f3850402-- <<< RESPONSE >>>
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: { Pragma: no-cache Cache-Control: no-cache Date: Thu, 21 Jun 2012 00:21:40 GMT Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Content-Length: 658 Content-Type: multipart/batch Expires: -1 }
--3d1ba137-ea6a-40d9-8e34-1b8812394baa Content-Type: application/http; msgtype=response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8
["Hello","world!"]
--3d1ba137-ea6a-40d9-8e34-1b8812394baa Content-Type: application/http; msgtype=response
HTTP/1.1 404 Not Found Content-Type: application/json; charset=utf-8
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost/foo/bar'."}
--3d1ba137-ea6a-40d9-8e34-1b8812394baa Content-Type: application/http; msgtype=response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8
"world!" --3d1ba137-ea6a-40d9-8e34-1b8812394baa--

As you can see, our batch was successfully run, and the results show what we'd expected (the two real API calls returned back 200 with their data, and the bogus request we threw in the middle returns back a 404).
正如我們所看到的,批處理成功地運行了,並且顯示了我們所期望的結果(兩個真正的API調用返回了帶有其數據的200狀態碼,而在中間壓入的偽造請求返回了404狀態碼)。


看完此文如果覺得有所收獲,請給個推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM