【问题标题】:Nested change sets in a batch payload are not supported : Odata - asp.net web api 2不支持批处理负载中的嵌套更改集:Odata - asp.net web api 2
【发布时间】:2015-06-12 09:34:23
【问题描述】:

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

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 客户端来发布一个批次。

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 添加到批处理的每个请求中。

发现“Content-Transfer-Encoding”标头丢失或无效。必须为每个批处理操作指定 'Content-Transfer-Encoding' 标头,其值必须为 'binary'

我能够在一批中合并多个 GET,并且正在为所有请求获取数据。但是对于帖子(在我的情况下是父母和孩子),我得到以下例外;

不支持批处理负载中的嵌套更改集。

这是使用 asp.net web API2 的 oData 的限制还是我在这里遗漏了什么?

【问题讨论】:

    标签: entity-framework odata asp.net-web-api2


    【解决方案1】:

    我终于解决了这个问题。

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

    错误 1

    “Content-Type”标头值“application/http; msgtype=request' 无效。当这是变更集的开始时,该值必须是“multipart/mixed”;否则它必须是“application/http”。

    错误 2

    内容类型“multipart/mixed”指定批处理负载;但是,负载不包含批处理边界或包含多个边界。在 OData 中,批处理负载内容类型必须在“内容类型的boundary'参数。

    错误 3

    不支持批处理负载中的嵌套更改集。

    错误 3 花了大部分时间从 MSDN 中找到这个 URL,它谈到了添加以下 Header Content Types

    客户端请求 ID

    return-client-request-id

    内容 ID

    数据服务版本

    将上述四种类型添加到标题后,错误 #3 消失了,我的批处理正在工作。

    来自 MSDN 的 URL 说;

    批处理服务中的添加任务请求支持以下概念:

    每个请求都必须包含一个唯一的 Content-ID MIME 部分标头。此标头与响应中的操作结果一起返回,可用于将单个请求与其响应匹配。

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

    批处理服务为有效的添加任务请求返回 HTTP 状态代码 202(已接受)。然后,服务器将流式传输各个操作的结果。

    批处理服务可以重新排序这些添加任务请求的响应。客户端需要使用 Content-Id MIME 部分头来匹配响应对应的请求。响应包含每个操作的结果和错误信息。如果在添加任务请求期间服务器超时或连接关闭,则该请求可能已部分或全部处理,或者根本没有处理。在这种情况下,用户应重新发出请求。请注意,在重新发出请求时,由用户正确处理失败。例如,用户在重试时应该使用相同的任务名称,这样如果之前的操作成功,重试就不会意外创建额外的任务。

    一个添加任务请求最多可以包含 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
    

    【讨论】:

    • 您能解释一下您是如何解决错误 2 的吗?
    • 非常感谢阿尼尔
    猜你喜欢
    • 2014-10-18
    • 2012-06-20
    • 2013-10-23
    • 2017-10-11
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 2012-05-23
    • 1970-01-01
    相关资源
    最近更新 更多