locked
Reading multipart response and get the different contents as objects RRS feed

  • Question

  • User-1978557453 posted

    Hi

    I receive a OData batch reponse message which content is multipart, but I can't figure how to cleanly read the contents. My response is like that:

    // batchRequest is a HttpRequestMessage, http is an HttpClient
    HttpResponseMessage response = await http.SendAsync(batchRequest);

    If I do the test to read the full content:

    string fullResponse = await response.Content.ReadAsStringAsync();

    The fullResponse string contains:

    --batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16
    Content-Type: application/http
    Content-Transfer-Encoding: binary
    
    HTTP/1.1 200 OK
    DataServiceVersion: 3.0;
    Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8
    
    {\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"5f6851c3-99cc-4a89-936d-4bb44fa78a34\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":\"NRF\",\"dirSyncEnabled\":null,\"displayName\":\"Patrick Test\",\"facsimileTelephoneNumber\":null,\"givenName\":\"Patrick\",\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":\"test\",\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"patricktest\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":{\"password\":null,\"forceChangePasswordNextLogin\":true,\"enforceChangePasswordPolicy\":false},\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-02T08:37:24Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":\"California\",\"streetAddress\":null,\"surname\":\"Test\",\"telephoneNumber\":\"666\",\"thumbnailPhoto@odata.mediaEditLink\":\"directoryObjects/5f6851c3-99cc-4a89-936d-4bb44fa78a34/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"patricktest@XXX.onmicrosoft.com\",\"userType\":\"Member\"}
    --batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16
    Content-Type: application/http
    Content-Transfer-Encoding: binary
    
    HTTP/1.1 200 OK
    DataServiceVersion: 3.0;
    Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8
    
    {\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"dd35d761-e6ed-44e7-919f-f3b1e54eb7be\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":null,\"dirSyncEnabled\":null,\"displayName\":\"Max Admin\",\"facsimileTelephoneNumber\":null,\"givenName\":null,\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":null,\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"maxadmin\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":null,\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-05T15:11:51Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":null,\"streetAddress\":null,\"surname\":null,\"telephoneNumber\":null,\"thumbnailPhoto@odata.mediaEditLink\":\"directoryObjects/dd35d761-e6ed-44e7-919f-f3b1e54eb7be/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"maxadmin@XXX.onmicrosoft.com\",\"userType\":\"Member\"}
    --batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16--

    I try to get all the separate contents like that:

    var multipartContent = await response.Content.ReadAsMultipartAsync();
    foreach (HttpContent currentContent in multipartContent.Contents) {
         var testString = currentContent.ReadAsStringAsync();
    }

    The test string contains, for the first content:

    HTTP/1.1 200 OK
    DataServiceVersion: 3.0;
    Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8
    
    {\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"5f6851c3-99cc-4a89-936d-4bb44fa78a34\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":\"NRF\",\"dirSyncEnabled\":null,\"displayName\":\"Patrick Test\",\"facsimileTelephoneNumber\":null,\"givenName\":\"Patrick\",\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":\"Ami des dipt\\u00e8res\",\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"patricktest\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":{\"password\":null,\"forceChangePasswordNextLogin\":true,\"enforceChangePasswordPolicy\":false},\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-02T08:37:24Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":\"California\",\"streetAddress\":null,\"surname\":\"Test\",\"telephoneNumber\":\"666\",\"thumbnailPhoto@odata.mediaEditLink\":\"directoryObjects/5f6851c3-99cc-4a89-936d-4bb44fa78a34/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"patricktest@XXX.onmicrosoft.com\",\"userType\":\"Member\"}

    What I need is to have this string response as an object, in order to have the status code (here 200), the JSON content, etc. as properties. I've tried a lot of things, like

    var test = await currentContent.ReadAsAsync<HttpResponseMessage>()
    

    But all unsuccessfull... If someone can guide me

    Thanks,

    Max

    Thursday, January 19, 2017 2:19 PM

Answers

  • User1031100566 posted

    I ran into the same issue and found that you need to add the msgtype parameter.  Below is my code

    foreach (var httpContent in responses)
    {
    if (httpContent.Headers.ContentType.MediaType.Equals("application/http", StringComparison.OrdinalIgnoreCase) &&
    !httpContent.Headers.ContentType.Parameters.Any(p => p.Name.Equals("msgtype", StringComparison.OrdinalIgnoreCase) && p.Value.Equals("response", StringComparison.OrdinalIgnoreCase)))
    {
    httpContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("msgtype", "response"));
    }
    }

    Then ReadAsHttpResponseMessageAsync() will work

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, March 2, 2017 5:18 PM

All replies

  • User-2057865890 posted

    Hi Max,

    You could define a class for the response from odata and then use Json.NET to deserialize into that class.

    reference: http://stackoverflow.com/a/39163010

    Best Regards,

    Chris

    Monday, January 23, 2017 6:11 AM
  • User-1978557453 posted

    Hello Chris

    Thanks for your answer. I'm aware that I can parse manually the response and build my own object, but I would prefer to avoid it (and that's why I ask it here).

    But unfortunately, because I don't have the hand on the Web API, maybe it's the only solution.

    Regards,

    Max

    Tuesday, January 24, 2017 9:07 AM
  • User-2057865890 posted

    Hi MAx,

    You could use ReadAsHttpResponseMessageAsync methods on the Content property of the response.

    var multipartContent = await response.Content.ReadAsMultipartAsync();
    foreach (HttpContent currentContent in multipartContent.Contents) {
         //Extracts each of the individual Http responses
         var testResponse = currentContent.ReadAsHttpResponseMessageAsync();
    }

    reference:

    https://damienbod.com/2014/03/09/data-transfers-with-web-api-batching-and-protobuf/

    https://blogs.msdn.microsoft.com/webdev/2013/11/01/introducing-batch-support-in-web-api-and-web-api-odata/

    Best Regards,

    Chris

    Tuesday, February 7, 2017 7:53 AM
  • User-1978557453 posted

    Hi Chris

    Thanks for your answer.

    I've already have tested this solution (and also tried all the possible ways to read the content that the object offers). It does not work because the multipart content contents do not have a header with value "application/http; msgtype=response".

    It throws an exception with that message:

    "Invalid 'HttpContent' instance provided. It does not have a content type header with a value of 'application/http; msgtype=response'.\r\nParameter name: content"

    I've worked on other subjects this past days but right now I'm really working on a manual parsing solution of the response.

    FYI if it can help, the Web API is the Azure AD Graph one.

    Regards,

    Max

    Wednesday, February 8, 2017 1:52 PM
  • User1031100566 posted

    I ran into the same issue and found that you need to add the msgtype parameter.  Below is my code

    foreach (var httpContent in responses)
    {
    if (httpContent.Headers.ContentType.MediaType.Equals("application/http", StringComparison.OrdinalIgnoreCase) &&
    !httpContent.Headers.ContentType.Parameters.Any(p => p.Name.Equals("msgtype", StringComparison.OrdinalIgnoreCase) && p.Value.Equals("response", StringComparison.OrdinalIgnoreCase)))
    {
    httpContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("msgtype", "response"));
    }
    }

    Then ReadAsHttpResponseMessageAsync() will work

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, March 2, 2017 5:18 PM