不支持批处理负载中的嵌套更改集: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
我在配置中启用了批处理路由,请参考下面的代码。
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