zoukankan      html  css  js  c++  java
  • web api 2 学习笔记 (OData Batch request)

    之前介绍过OData 中实现RPC的写法,今天在来一个批量操作。

    参考 : https://damienbod.wordpress.com/2014/08/14/web-api-odata-v4-batching-part-10/

    http://www.odata.org/getting-started/advanced-tutorial/

    public static void Register(HttpConfiguration config)
    {
        DefaultODataBatchHandler odataBatchHandler = new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer);
        odataBatchHandler.MessageQuotas.MaxOperationsPerChangeset = 10;
        odataBatchHandler.MessageQuotas.MaxPartsPerBatch = 10;
        config.MapODataServiceRoute("odata", "api", GetModel(), odataBatchHandler);           
    }

    填入DefaultODataBatchHandler就可以了.

    前端js

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://localhost:4274/api/$batch", true);
    xhr.setRequestHeader("Content-Type", "multipart/mixed; boundary=batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03");
    xhr.setRequestHeader("OData-Version", "4.0");
    xhr.setRequestHeader("singleTransaction", "true");
    
    var body = [];
    //POST
    body.push('--batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03');
    body.push('Content-Type: multipart/mixed; boundary=changeset_54ac09ec-f437-4b08-9925-fd42ed7bd58f');
    body.push('');
    body.push('--changeset_54ac09ec-f437-4b08-9925-fd42ed7bd58f');
    body.push('Content-Type: application/http');
    body.push('Content-Transfer-Encoding: binary');
    body.push('Content-ID: 1');
    body.push('');
    body.push('POST http://localhost:4274/api/products HTTP/1.1');
    body.push('OData-Version: 4.0');
    body.push('Content-Type: application/json;odata.metadata=minimal');
    body.push('Accept: application/json;odata.metadata=minimal');
    body.push('');
    body.push('{"code":"mk100"}');
    body.push('--changeset_54ac09ec-f437-4b08-9925-fd42ed7bd58f--');
    
    //PUT
    body.push('--batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03');
    body.push('Content-Type: multipart/mixed; boundary=changeset_2346da5e-88c9-4aa5-a837-5db7e1368147');
    body.push('');
    body.push('--changeset_2346da5e-88c9-4aa5-a837-5db7e1368147');
    body.push('Content-Type: application/http');
    body.push('Content-Transfer-Encoding: binary');
    body.push('Content-ID: 2');
    body.push('');
    body.push('PUT http://localhost:4274/api/products(1) HTTP/1.1');
    body.push('OData-Version: 4.0');
    body.push('Content-Type: application/json;odata.metadata=minimal');
    body.push('Accept: application/json;odata.metadata=minimal');
    body.push('');
    body.push('{"id":1,"code":"mk100"}');
    body.push('--changeset_2346da5e-88c9-4aa5-a837-5db7e1368147--');
    
    
    //DELETE
    body.push('--batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03');
    body.push('Content-Type: multipart/mixed; boundary=changeset_2346da5e-88c9-4aa5-a837-5db7e1368142');
    body.push('');
    body.push('--changeset_2346da5e-88c9-4aa5-a837-5db7e1368142');
    body.push('Content-Type: application/http');
    body.push('Content-Transfer-Encoding: binary');
    body.push('Content-ID: 3');
    body.push('');
    body.push('DELETE http://localhost:4274/api/products(1) HTTP/1.1');
    body.push('OData-Version: 4.0');
    body.push('Content-Type: application/json;odata.metadata=minimal');
    body.push('Accept: application/json;odata.metadata=minimal');
    body.push('');
    body.push('--changeset_2346da5e-88c9-4aa5-a837-5db7e1368142--');
                
    //GET
    body.push('--batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03');
    body.push('Content-Type: application/http');
    body.push('Content-Transfer-Encoding: binary');
    body.push('Content-ID: 4');
    body.push('');
    body.push('GET http://localhost:4274/api/products HTTP/1.1');
    body.push('OData-Version: 4.0');
    body.push('Content-Type: application/json;odata.metadata=minimal');
    body.push('Accept: application/json;odata.metadata=minimal');
    body.push('');
    
    body.push('--batch_ebdc0b88-eeb1-4dd6-b170-74331f39bd03--');
    body.push('');
    var data = body.join("
    ");
    xhr.send(data);

    从上面代码可以看出,我们所有的请求需要通过一个大请求来包装,把所有的小请求用string写进大请求的body就可以了。

    需要特别注意的事string的格式,连空行都是非常重要的哦!

    参考 http://www.odata.org/documentation/odata-version-3-0/batch-processing/

    虽然这是v3的但是可以看一下, 2.2 Batch Request Body 

    请求分2中,一种叫changeset,一种叫 operation 

    changeset 是指那些会改变资源的请求(e.g. POST,PUT,DELETE,ACTION), operation 是指不会改变资源的请求 (e.g. GET,FUNCTION) 

    代码中可以看出来,这2种写法会有不同。

    通常我们在做批量操作时希望会有transaction

    这时我们可以扩展 DefaulODataBatchHandle 

    public class ODataBatchHandlerSingleTransaction : DefaultODataBatchHandler
    {
        public ODataBatchHandlerSingleTransaction(HttpServer httpServer)
            : base(httpServer)
        {
        }
    
        public async override Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests,CancellationToken cancellation)
        {
            if (requests == null) { throw new ArgumentNullException("requests"); }            
            IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
                             
            try
            {
                using (DB db = new DB())
                {
                    using (DbContextTransaction trans = db.Database.BeginTransaction())
                    {
                        foreach (ODataBatchRequestItem request in requests)
                        {
                            var changeSetResponse = (ChangeSetResponseItem)await request.SendRequestAsync(Invoker, cancellation);
                            responses.Add(changeSetResponse);
                        }
                        bool isAllOk = responses.All(response => ((ChangeSetResponseItem)(response)).Responses.All(r => r.IsSuccessStatusCode));
                        if (isAllOk)
                        {
                            trans.Commit();
                        }
                        else
                        {
                            trans.Rollback();
                        }
                    }
                }
            }
            catch
            {
                foreach (ODataBatchResponseItem response in responses)
                {
                    if (response != null)
                    {
                        response.Dispose();
                    }
                }
                throw;
            }
            return responses;
        }      
    } 

    拦截以后我们就可以在这一层创建 database Context 和 transaction , controller 内就可以通过任何方式来获取到这里的 context 来做使用. 

    比如可以使用 Request.Items 来保存传值. (注 : httpRequest 和 httpRequestMessage 是不同的,我们在controller使用的是 message 哦)  

    还有一点要特别注意的是,如果你需要transaction就不应该有请求,因为GET 请求会在 ExecuteRequestMessagesAsync 之后才执行,如果这时我们释放掉了 database context 那么就会有问题了.

  • 相关阅读:
    P20 HTTP 方法的安全性与幂等性
    P19 查询参数
    P18 写代码:过滤和搜索
    P17 过滤和搜索
    P16 HTTP HEAD
    golang的json操作[转]
    Android中的Service 与 Thread 的区别[转]
    iOS的block内存管理
    Go并发编程基础(译)
    golang闭包里的坑
  • 原文地址:https://www.cnblogs.com/keatkeat/p/4806324.html
Copyright © 2011-2022 走看看