首页 > 解决方案 > Why is my OData 4 Batch not picking up the request body of the last request

问题描述

I am working on an ASP.NET Core 2.2 API using the Microsoft.AspNetCore.OData NuGet v7.1.0 and I am trying to test OData batch using Postman v7.0.5.

The problem I am having is that it always fails to see the data in the last POST request in my batch. In the response, I get "201 Created" for every post except the last on, which returns "400 Bad Request" because it is not picking up the data in the last request body.

Here is the relevant section of my Startup.cs where I enable OData Batch handling;

app.UseODataBatching();
app.UseMvc(routeBuilder =>
{
    routeBuilder
        .MapODataServiceRoute("ODataRoutes", "api/v1",
             modelBuilder.GetEdmModel(app.ApplicationServices), 
             new DefaultODataBatchHandler());
});

In Postman, I have a POST request to

{{url}}/api/v1/$batch

and in the Request -->Headers section, I have a Content-Type header set to

multipart/mixed; boundary=batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0

The Body of the request is set to "Raw" and "Text"

Below is the request body;

--batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /api/v1/AddressComplianceCode HTTP/1.1
OData-Version: 4.0
Content-Type: application/json
Accept: application/json;odata.metadata=minimal

{
  "Code": "Z1",
  "Description": "Test Batch Z1",
  "Active": true
}

--batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /api/v1/AddressComplianceCode HTTP/1.1
OData-Version: 4.0
Content-Type: application/json
Accept: application/json;odata.metadata=minimal

{
  "Code": "Z2",
  "Description": "Test Batch Z2",
  "Active": true
}

--batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /api/v1/AddressComplianceCode HTTP/1.1
OData-Version: 4.0
Content-Type: application/json
Accept: application/json;odata.metadata=minimal

{
  "Code": "Z3",
  "Description": "Test Batch Z3",
  "Active": true
}

--batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0
Content-Type: application/http
Content-Transfer-Encoding: binary

POST /api/v1/AddressComplianceCode HTTP/1.1
OData-Version: 4.0
Content-Type: application/json
Accept: application/json;odata.metadata=minimal

{
  "Code": "Z4",
  "Description": "Test Batch Z4",
  "Active": true
}

--batch_abbe2e6f-e45b-4458-9555-5fc70e3aebe0--

And here is the response;

--batchresponse_f2c84aaf-dc39-4f20-8da0-881f402436fa
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Location: https://localhost:44331/api/v1/AddressComplianceCode('Z1')
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
OData-Version: 4.0

{"@odata.context":"https://localhost:44331/api/v1/$metadata#AddressComplianceCode/$entity","Code":"Z1","Description":"Test Batch Z1","MarkedForRetirement":false,"RetirementDate":null,"LastModifiedDate":"2019-03-12T10:19:20.9434728-04:00","LastModifiedBy":null,"CreatedDate":"2019-03-12T10:19:20.9434728-04:00","CreatedBy":null,"Delete":false,"Active":true}
--batchresponse_f2c84aaf-dc39-4f20-8da0-881f402436fa
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Location: https://localhost:44331/api/v1/AddressComplianceCode('Z2')
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
OData-Version: 4.0

{"@odata.context":"https://localhost:44331/api/v1/$metadata#AddressComplianceCode/$entity","Code":"Z2","Description":"Test Batch Z2","MarkedForRetirement":false,"RetirementDate":null,"LastModifiedDate":"2019-03-12T10:19:21.2241031-04:00","LastModifiedBy":null,"CreatedDate":"2019-03-12T10:19:21.2241031-04:00","CreatedBy":null,"Delete":false,"Active":true}
--batchresponse_f2c84aaf-dc39-4f20-8da0-881f402436fa
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 201 Created
Location: https://localhost:44331/api/v1/AddressComplianceCode('Z3')
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
OData-Version: 4.0

{"@odata.context":"https://localhost:44331/api/v1/$metadata#AddressComplianceCode/$entity","Code":"Z3","Description":"Test Batch Z3","MarkedForRetirement":false,"RetirementDate":null,"LastModifiedDate":"2019-03-12T10:19:21.5068813-04:00","LastModifiedBy":null,"CreatedDate":"2019-03-12T10:19:21.5068813-04:00","CreatedBy":null,"Delete":false,"Active":true}
--batchresponse_f2c84aaf-dc39-4f20-8da0-881f402436fa
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 400 Bad Request
Content-Type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
OData-Version: 4.0

{"error":{"code":"","message":"The input was not valid.","details":[{"code":"","message":"The input was not valid."}]}}
--batchresponse_f2c84aaf-dc39-4f20-8da0-881f402436fa--

No matter how many POST sections I add to the request ( I have tested 2, 3 and 4), the last request always fails to pass the request body values.

I have reviewed the Batch advanced tutorial at www.odata.org site, as well as all of the relevant SO posts I could find. I also tried the Github issues pages using the filter;

is:issue is:open batch 

All with no luck so far.

What am I missing here?

标签: asp.net-coreodatapostmanasp.net-core-2.1

解决方案


Based upon my own experiences, I'd guess it's because you're sending LFs with Postman, rather than CRLFs.

Postman will send whatever newline you've entered (perhaps pasted in from something that prefers LF) , but multipart/mixed data requires CRLF. Sending just the LF confuses the ODataMultipartMixedBatchReader about whether a boundary line is an end-boundary or not, and causes the end-boundary marker to be added to the request. This in turn confuses the model binder on the ASP.NET Core side of things, which can't deserialize the request body.


推荐阅读