2
votes

I looked at the MS Source code according to their interpretation the HttpClient itself does not have "Content-Type" only the content should have content-type. Seems logical except when you're dealing with MultipartFormDataContent. MultipartFormDataContent completely ignores the following code:

string boundary = "--" + GenerateRandomString();
using (var content = new MultipartFormDataContent(boundary))
{
    content.Headers.ContentType = new MediaTypeHeaderValue($"multipart/form-data");
    content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("boundry", boundary));
    ...
}

No "Content-Type" is present in the request. And also ignores:

string boundary = "--" + GenerateRandomString();
using (var content = new MultipartFormDataContent(boundary))
{
    content.Headers.Remove("Content-Type");
    content.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
    ...
}    

Attemting to set it at on the HttpClient

client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);

throws the following error:

System.InvalidOperationException: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'

I can find plenty of examples of how to do this using StringContent but none with MultipartFormDataContent. MultipartFormDataContent allows setting the Content-Type and Content-Disposition with each field, I need this more at the client level. I need a header that looks something like this:

accept: application/json
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Authorization: Basic ZXlKbGRDSTZJakUxTWpZNU9UTXpOTnpkMjl5WkNJNklqa3haRGxtWkdKa1lUazRaVEJqWmpsalpUaGxNV1V3TXpOalxuWmpCbE1tVXhJaXdpZFhObGNpSTZJbUZrYldsdUluMD1cbjo=
Cache-Control: no-cache

Many non-Microsoft APIs require the "boundary" tag so it can distinguish the individual fields of data being sent. The validation here on the request seems a little over the top. Even TryAddWithoutValidation doesn't work (maybe a bug?). I realize that it may be possible to interpret RFC7578 in a way that says it shouldn't be required but flat out not allowing it doesn't seem right to me either. Anyone else ever run into this issue and solve it.

1
But MultipartFormDataContent already sets Content-Type header correctly by itself (with boundary), so seems no additional efforts required? I just tested with HttpClient in .NET 4.6 and it sends Content-Type just fine - Evk
Hmm, it doesn't do that for me. I am using latest .Net Standard, so maybe it actually is a bug? I thought that it should do it automatically and I was surprised not to see it in the request header. I recorded the request with Fiddler and also used HttpLoggingHandler to dump the request out to file. - user2033791
@user2033791 what runtime did you target? .NET Standard isnt' a runtime. HttpClient itself is available through the System.Net.Http package. Which version did you use? - Panagiotis Kanavos
I can't reproduce any issue using MultipartFormDataContent even when the call to HttpClient.PostAsync is in a .NET Standard 2.0 library. The boundary is there and so is the content type. No header manipulations were needed - Panagiotis Kanavos
How do you know there is a problem? Did you inspect the HTTP request with a proxy like Fiddler ir did you get a server error and assumed it's the boundary? - Panagiotis Kanavos

1 Answers

0
votes

Initially I thought this was an HttpClient bug. I added logging to capture the request and the response. That logging was missing headers which lead me to believe that the missing "multi-part/form-data" content header was the issue and the reason the API I'm using kept telling me it couldn't find a required field. It turns out to be an issue with how the API handles the data it's sent when it's multi-part/form-data. After comparing both my HttpWebRequest and the HttpClient request in fiddler I discovered the following difference in the data being sent:

HttpWebRequest

----vekhftkcthxr
Content-Disposition: form-data; name="name";

d30-20180524

HttpClient

----bcgifxyjkmkw
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=name

d30-20180524

I build the HttpWebRequest manually so I included the quotes and ending semi-colons. The HttpClient request is built for me and does not include the extra quotes and semi-colon. So the API I am using does not play well with request being generated by the HttpClient even though the request is technically correct.

Thanks to Panagiotis Kanavos for showing me my error.