不支持批处理负载中的嵌套更改集:Odata - asp.net web api 2

Nested change sets in a batch payload are not supported : Odata - asp.net web api 2

我在配置中启用了批处理路由,请参考下面的代码。

namespace WebAPI {
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Services.Replace(typeof(IExceptionHandler), new CustomExceptionHandler());

            config.MapODataServiceRoute(routeName: "OData",
                routePrefix: "",
                model: APIConfiguration.GetModel(),
                batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
        }
} } 

我正在使用下面的代码来使用 http 客户端 post 一个批处理。

Dim batchContent As New MultipartContent("mixed", "--testDataBoundary---")

'Parent
Dim RqP As New HttpRequestMessage(HttpMethod.Put, String.Format("{0}Projects({1})/", APIURI, projectRequest.project.ProjectID))
RqP.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(projectRequest.project, microsoftDateFormatSettings))
Dim ProjectContent As New HttpMessageContent(RqP)
If ProjectContent.Headers.Contains("Content-Type") Then ProjectContent.Headers.Remove("Content-Type")
ProjectContent.Headers.Add("Content-Type", "multipart/mixed; boundary=--testPTDataBoundary---")
'ProjectContent.Headers.Add("Content-Type", "application/http")
ProjectContent.Headers.Add("Content-Transfer-Encoding", "binary")

If ProjectContent.Headers.Contains("contentTypeMime-Part") Then ProjectContent.Headers.Remove("contentTypeMime-Part")
'ProjectContent.Headers.Add("contentTypeMime-Part", "Content-Type:application/http;")
ProjectContent.Headers.Add("contentTypeMime-Part", "Content-Type:multipart/mixed;")

batchContent.Add(ProjectContent)

'Child1
Dim report As Report = projectRequest.project.Reports.Take(1).First()
Dim RqR As New HttpRequestMessage(HttpMethod.Put, String.Format("{0}Reports({1})/", APIURI, report.ReportID))
RqR.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(report, microsoftDateFormatSettings))
Dim ReportContent As New HttpMessageContent(RqR)
If ReportContent.Headers.Contains("Content-Type") Then ReportContent.Headers.Remove("Content-Type")
ReportContent.Headers.Add("Content-Type", "multipart/mixed; boundary=--testPTDataBoundary---")
'ReportContent.Headers.Add("Content-Type", "application/http")
ReportContent.Headers.Add("Content-Transfer-Encoding", "binary")

If ReportContent.Headers.Contains("contentTypeMime-Part") Then ReportContent.Headers.Remove("contentTypeMime-Part")
'ReportContent.Headers.Add("contentTypeMime-Part", "Content-Type:application/http;")
ReportContent.Headers.Add("contentTypeMime-Part", "Content-Type:multipart/mixed;")

batchContent.Add(ReportContent)


Dim batchRequest As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Post, APIURI& "/$batch/")
batchRequest.Content = batchContent

Dim APIResponseBatch = Await Client.SendAsync(batchRequest)
Dim streamProvider = APIResponseBatch.Content.ReadAsMultipartAsync().Result()

For Each cnt As HttpContent In streamProvider.Contents
  lblErrorMsg.Text &= cnt.ReadAsStringAsync().Result
Next

最初我收到以下异常,然后我将 contentTypeMime-Part 添加到批处理的每个请求中。

A missing or invalid 'Content-Transfer-Encoding' header was found. The 'Content-Transfer-Encoding' header must be specified for each batch operation, and its value must be 'binary'

我可以在一批中合并多个 GET,并且正在为所有请求获取数据。但是对于 post(Parent 和 Children 在我的例子中)我得到了以下例外;

Nested change sets in a batch payload are not supported.

使用 asp.net web API2 的 oData 是否有此限制,或者我在这里遗漏了什么?

我终于解决了这个问题。

下面给出的序列中存在以下错误,我继续添加 header,最后我能够批量处理两个 PUT 请求。

错误 1

"Content-Type’ header value "application/http; msgtype=request' 无效。当这是更改集的开始时,该值必须是 "multipart/mixed'; otherwise it must be "application/http'.

错误 2

内容类型"multipart/mixed’ specifies a batch payload; however, the payload either does not include a batch boundary or includes more than one boundary. In OData, batch payload content types must specify exactly one batch boundary in the "boundary' 内容类型的参数。

错误 3

不支持批有效负载中的嵌套更改集。

错误 3 花了大部分时间从 MSDN 中找出这个 URL,它谈到添加以下 Header 内容类型

client-request-id

return-client-request-id

Content-ID

数据服务版本

将以上四种类型添加到 header 后,错误 #3 消失了,我的批处理工作正常。

来自 MSDN 的 URL 说;

在 Batch 服务中添加任务请求支持以下概念:

每个请求必须包含唯一的 Content-ID MIME 部分 header。此 header 与响应中的操作结果一起 return 编辑,可用于将单个请求与其响应相匹配。

如果任何请求包含一组无效的 header 或包含批处理中不支持的操作,则批处理服务 return 将显示 HTTP 状态代码 400(错误请求)。

Batch 服务 return 为有效的“添加任务”请求提供 HTTP 状态代码 202(已接受)。然后服务器将流式传输各个操作的结果。

Batch 服务可以re-order 响应这些添加任务请求。 Content-IdMIME部分header需要Client用来匹配response对应的request。响应包含每个 operation.If 服务器超时或连接在添加任务请求期间关闭的结果和错误信息,请求可能已部分或全部处理,或者根本没有处理。在这种情况下,用户应该 re-issue 请求。请注意,在 re-issuing 请求时正确处理失败取决于用户。例如,用户在重试时应该使用相同的任务名称,这样如果之前的操作成功了,重试就不会意外地创建额外的任务。

一个添加任务请求最多可以包含 100 个操作。

添加任务请求中的所有任务必须属于同一工作项并且

代码

Private Async Function SaveProjectAsBatch() As Threading.Tasks.Task
        Using Client As New HttpClient()
            Dim APIhostUri As String = "http://localhost:2025/"
            Dim APIResponse As HttpResponseMessage
            Client.BaseAddress = New Uri(APIhostUri)
            Dim content As HttpContent

            'If date serialization is required              
            Dim microsoftDateFormatSettings As JsonSerializerSettings = New JsonSerializerSettings With {
                                .DateFormatHandling = DateFormatHandling.IsoDateFormat,
                                .DateParseHandling = DateParseHandling.DateTimeOffset, .DateTimeZoneHandling = DateTimeZoneHandling.Utc
                                }

            ' Batch Process
            Dim boundary As String = "--changeset_ProjectData"
            Dim batchContent As New MultipartContent("mixed", "--batch" & boundary)

            'request 1
            Dim RqP As New HttpRequestMessage(HttpMethod.Put, "Url1(key)/")
            RqP.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(RequestContent, microsoftDateFormatSettings))
            If RqP.Content.Headers.Contains("Content-Type") Then RqP.Content.Headers.Remove("Content-Type")
            RqP.Content.Headers.Add("Content-Type", "application/json")
            Dim Request1 As New HttpMessageContent(RqP)
            If Request1.Headers.Contains("Content-Type") Then Request1.Headers.Remove("Content-Type")
            Request1.Headers.Add("Content-Type", "application/http")
            Request1.Headers.Add("Content-Transfer-Encoding", "binary")
            Request1.Headers.Add("client-request-id", "12345678")
            Request1.Headers.Add("return-client-request-id", "True")
            Request1.Headers.Add("Content-ID", "1")
            Request1.Headers.Add("DataServiceVersion", "3.0")
            If Request1.Headers.Contains("contentTypeMime-Part") Then Request1.Headers.Remove("contentTypeMime-Part")
            Request1.Headers.Add("contentTypeMime-Part", "Content-Type:application/http;")

            batchContent.Add(Request1)

            'request 2
            Dim RqR As New HttpRequestMessage(HttpMethod.Put, "Url2(Key)/")
            RqP.Content = New StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(Request1Content, microsoftDateFormatSettings))
            If RqR.Content.Headers.Contains("Content-Type") Then RqR.Content.Headers.Remove("Content-Type")
            RqR.Content.Headers.Add("Content-Type", "application/json")
            Dim Request2 As New HttpMessageContent(RqR)

            If Request2.Headers.Contains("Content-Type") Then Request2.Headers.Remove("Content-Type")
            Request2.Headers.Add("Content-Type", "application/http")
            Request2.Headers.Add("Content-Transfer-Encoding", "binary")
            Request2.Headers.Add("client-request-id", "99999")
            Request2.Headers.Add("return-client-request-id", "True")
            Request2.Headers.Add("Content-ID", "2")
            Request2.Headers.Add("DataServiceVersion", "3.0")

            If Request2.Headers.Contains("contentTypeMime-Part") Then Request2.Headers.Remove("contentTypeMime-Part")
            Request2.Headers.Add("contentTypeMime-Part", "Content-Type:application/http;")
            batchContent.Add(Request2)


            Dim batchRequest As HttpRequestMessage = New HttpRequestMessage(HttpMethod.Post, APIhostUri & "/$batch/")
            batchRequest.Content = batchContent


            Dim APIResponseBatch = Await Client.SendAsync(batchRequest)
            Dim streamProvider = APIResponseBatch.Content.ReadAsMultipartAsync().Result()

            For Each cnt As HttpContent In streamProvider.Contents
                Console.WriteLine(cnt.ReadAsStringAsync().Result)
            Next

        End Using
    End Function