Как установить заголовок Content-Type для запроса HttpClient с помощью MultipartFormDataContent?

Я посмотрел на исходный код MS в соответствии с их интерпретацией, сам HttpClient не имеет «типа контента», только контент должен иметь тип контента. Кажется логичным, за исключением случаев, когда вы имеете дело с MultipartFormDataContent. MultipartFormDataContent полностью игнорирует следующий код:

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));
    ...
}

В запросе отсутствует "Content-Type". А также игнорирует:

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

Попытка установить его на HttpClient

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

выдает следующую ошибку:

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

Я могу найти множество примеров того, как это сделать с помощью StringContent, но ни одного с MultipartFormDataContent. MultipartFormDataContent позволяет установить Content-Type и Content-Disposition для каждого поля, мне это нужно больше на уровне клиента. Мне нужен заголовок, который выглядит примерно так:

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

Многие не-Microsoft API требуют тега «граница», чтобы он мог различать отдельные поля отправляемых данных. Проверка здесь по запросу кажется немного чрезмерной. Даже TryAddWithoutValidation не работает (может баг?). Я понимаю, что, возможно, можно интерпретировать RFC7578 таким образом, который говорит, что это не должно требоваться, но категорически не позволять этого мне тоже не кажется правильным. Кто-нибудь еще сталкивался с этой проблемой и решал ее.


person user2033791    schedule 23.05.2018    source источник
comment
Но MultipartFormDataContent уже сам по себе правильно устанавливает заголовок Content-Type (с границей), так что никаких дополнительных усилий не требуется? Я только что протестировал HttpClient в .NET 4.6, и он отлично отправляет Content-Type.   -  person Evk    schedule 23.05.2018
comment
Хм, это не делает это для меня. Я использую последний стандарт .Net, так что, может быть, это действительно ошибка? Я думал, что он должен делать это автоматически, и был удивлен, не увидев его в заголовке запроса. Я записал запрос с помощью Fiddler, а также использовал HttpLoggingHandler для вывода запроса в файл.   -  person user2033791    schedule 24.05.2018
comment
@ user2033791 на какую среду выполнения вы нацелились? .NET Standard не является средой выполнения. Сам HttpClient доступен через пакет System.Net.Http. Какую версию вы использовали?   -  person Panagiotis Kanavos    schedule 24.05.2018
comment
Я не могу воспроизвести какую-либо проблему с помощью MultipartFormDataContent, даже если вызов HttpClient.PostAsync находится в библиотеке .NET Standard 2.0. Граница есть, как и тип контента. Никаких манипуляций с заголовком не потребовалось   -  person Panagiotis Kanavos    schedule 24.05.2018
comment
Как понять, что есть проблема? Вы проверили HTTP-запрос с помощью прокси-сервера, такого как Fiddler, или вы получили ошибку сервера и предположили, что это граница?   -  person Panagiotis Kanavos    schedule 24.05.2018


Ответы (1)


Сначала я подумал, что это ошибка HttpClient. Я добавил ведение журнала, чтобы зафиксировать запрос и ответ. В этом журнале отсутствовали заголовки, что навело меня на мысль, что отсутствующий заголовок содержимого «multi-part/form-data» был проблемой и причиной того, что API, который я использую, постоянно говорил мне, что не может найти необходимое поле. Оказывается, проблема заключается в том, как API обрабатывает данные, которые он отправляет, когда он состоит из нескольких частей/форм данных. После сравнения моего запроса HttpWebRequest и HttpClient в fiddler я обнаружил следующую разницу в отправляемых данных:

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

Я создаю HttpWebRequest вручную, поэтому я включил кавычки и конечные точки с запятой. Запрос HttpClient создан для меня и не включает дополнительные кавычки и точку с запятой. Таким образом, API, который я использую, плохо работает с запросом, генерируемым HttpClient, даже если запрос технически правильный.

Спасибо Панайотису Канавосу за то, что он показал мне мою ошибку.

person user2033791    schedule 24.05.2018
comment
Подтверждено как? .NET Standard не является целевой средой выполнения. Вы не можете создать исполняемый файл .NET Standard. На какую среду выполнения и версию вы ориентируетесь? Даже если вы поместите код в библиотеку .NET Standard, какую версию вы выбрали? - person Panagiotis Kanavos; 24.05.2018
comment
Нет воспроизведения в .NET Core 2.0. Использование MultipartFormDataContent само по себе отправляет границу - person Panagiotis Kanavos; 24.05.2018
comment
Не воспроизводится и тогда, когда код находится в библиотеке .NET Standard и вызывается консольным приложением. В обоих случаях есть тип контента и граница. Вы не можете ожидать исправления от Microsoft, если проблема не может быть воспроизведена - person Panagiotis Kanavos; 24.05.2018
comment
Он работает в проекте .Net 4.5 в Visual Studio. Это не работает, если вы создаете стандартную библиотеку .Net с проектом .Net Core 2.0. - person user2033791; 24.05.2018
comment
В этом сценарии также нет воспроизведения. Вы пытались опубликовать без попытки изменить заголовки? Какой код вы использовали для тестирования проблемы, какие версии пакетов вы использовали? Какова версия вашего пакета NETStandard.Library? В новом проекте библиотеки классов это 2.0.3. - person Panagiotis Kanavos; 24.05.2018
comment
Да, моя первая попытка была без попытки изменить заголовки. Когда все вызовы потерпели неудачу, я начал регистрировать запрос (заголовки и тело) и ответ. Ответ, который я видел от API, в который я публиковал, был: «Отсутствует обязательное поле XXXX». Это выглядело как странная ошибка, потому что я видел поля и данные, присутствующие в теле. Затем я заметил, что заголовок отсутствует, и это имело смысл, он не мог анализировать данные без границы поля. Тестовым проектом был .Net Core 2.0 с использованием Xunit. Клиент был .Net Standard 2.0.0 - person user2033791; 24.05.2018
comment
И повторяю, никакой репродукции. Попробуйте еще раз с помощью Fiddler вместо ведения журнала и проверьте фактическое содержание запроса. Создайте простое консольное приложение .NET Core 2.0 и простую библиотеку .NET Standard 2.0. - person Panagiotis Kanavos; 24.05.2018
comment
Ты прав. Заголовок отображается в скрипаче, но его нет в моем журнале HttpRequestMessage. Я буду исследовать дальше и изменить свой ответ. - person user2033791; 24.05.2018