Answered by:
Reading multipart response and get the different contents as objects

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/
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