這篇稍微簡單的紀錄一下,浪費掉我打電動認真陪家人的兩個放假天,那話也說在前面,根據查到的狀況,看起來,應該是Web API的BUG,但官方並沒有承諾甚麼時候會改好,只能誠心祈禱RTM的Web API 2能順利修正,所以,未來如果有遇到類似的問題,大家可以試著升級成最新版本看看。
最後,和這件事以外的另外一個補充,先不論這件事情,目前Web API OData的支援程度還是有很多地方沒完整實現,所以很容易踩到地雷,當然,官方還是在努力地將Web API越做越好;而如果害怕擔心,對於WCF又已經熟到不行的朋友,那可以考慮直接用WCF時做OData Endpoint吧=v=。
那這個問題是甚麼??..
主要是小弟在整合OData方案的時候,搭配JayData和Datajs來協助對OData Service來進行資料的管控與存取,一般的使用上,都沒遇到甚麼多大的問題 ( 也吃了不少苦….因為現在文件幾乎都是針對WCF… ),但最近在處理以下Code的時候,缺發生了一個比較棘手的問題;喔,對了,看不太懂此Code沒關係,簡單的說,我就是要利用JS來新增資料,先用add去新增到Context,然後再用JayData的Framework來saveChanges方法,一次更新。
var CustomerAgreementType = new InternalIMS.Model.CustomerAgreementType({ CustomerID: fkID }); CustomerAgreementType.ModifyDate = Date.now(); mydatabase.Agreement.add(model); mydatabase.CustomerAgreementType.add(CustomerAgreementType); mydatabase.saveChanges(function () { });
上面那個Code看不懂其實沒關係,總之,我想要的效果,就是利用一次的Request,來送出兩個新增的訊息( 以前的話,要送出兩次Request來達成新增。)
而之前的Web API連此功能都沒有…Orz..在Web API 2也已經提供了此功能,此功能就稱為Batching,有興趣的朋友,可以看這篇架設。(其實也只是多設定一行而已…)
但真正的問題在後面,我們先來看一下,要新增兩筆資料的Requst,如下圖,我們可以看到一次發送兩筆POST ( 因為我們要新增嘛… )
但當我們看Response的時候,我們可以看到發生了406 Not Acceptable錯誤。
當下發生這個錯誤,真的是把我搞死…後來才查到,原來是OData服務網址的錯誤,也就是這張圖的這個地方。
是的,其實是URL錯誤,上圖的URL的位置其實是http://localhost/Agreement和http://localhost/CustomerAgreementType,但實際上,我們的位置卻應該是在localhost/OData/Agreement之下…所以會返回406錯誤。
為了驗證真的是這個問題,所以小弟我直接用datajs來進行測試,Code如下。
OData.request({ requestUri: "/OData/$batch", method: "POST", data: { __batchRequests: [ { __changeRequests: [ { requestUri: "/OData/Agreement", method: "POST", data: model } ] }, { requestUri: "/OData/Agreement", method: "GET" } ] } }, function (data, response) { alert("ok"); }, function () { alert("request failed"); }, OData.batchHandler);
結果就很順利…我們可以看到POST後的網址已經正確了。
而Respose也正常了。
所以到這邊,就可以推測是這裡的問題了。
至於解法,目前小弟我查到的訊息,除了等Web API更新以外,要不就是自己重新定義處理器。
處理器的Code如下。( 此Code來源於JayData論壇的Fenderbender神人 )
public class PathFixODataBatchHandler : DefaultODataBatchHandler { public PathFixODataBatchHandler(HttpServer httpServer) : base(httpServer) { } public override async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken) { if (requests == null) { throw new System.ArgumentNullException("requests"); // Error.ArgumentNull("requests"); } IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>(); try { foreach (ODataBatchRequestItem request in requests) { fixRequestUri(request); responses.Add(await request.SendRequestAsync(Invoker, cancellationToken)); } } catch { foreach (ODataBatchResponseItem response in responses) { if (response != null) { response.Dispose(); } } throw; } return responses; } private void fixRequestUri(ODataBatchRequestItem request) { foreach (HttpRequestMessage req in ((ChangeSetRequestItem)request).Requests) { var oldUri = req.RequestUri; var newUriBuilder = new UriBuilder(oldUri); newUriBuilder.Path = "/odata" + newUriBuilder.Path; req.RequestUri = newUriBuilder.Uri; } } }
其實重點就是後面,將path加上"/odata"。
然後我們在Web API的Router Config再來調整一下,改用上面的Class。
config.Routes.MapODataRoute( routeName: "ODataRoute", routePrefix: "odata", model: GetModel(), //batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)); batchHandler: new PathFixODataBatchHandler(GlobalConfiguration.DefaultServer)); config.EnableQuerySupport();
這樣就可以了~~
後記
目前查出來的文章,都指向ASP.NET WEB API的問題XDD,因為除了JayData外,BreezeJS也有同樣的問題;但WCF下,就無此問題;但不管怎樣,有用到此技術的朋友們,可以稍微注意一下喔!!
參考網址
- http://www.odata.org/documentation/odata-v2-documentation/batch-processing/
- http://stackoverflow.com/questions/18813890/post-batch-request-with-breezejs
- http://jaydata.org/forum/viewtopic.php?f=3&t=300
- https://github.com/jaydata/jaydata/issues/131
- http://aspnetwebstack.codeplex.com/wikipage?title=Web%20API%20Request%20Batching&referringTitle=Specs