Я пытаюсь установить заголовок Content-Type
объекта HttpClient
, как того требует API, который я вызываю.
Я попытался установить Content-Type
, как показано ниже:
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://example.com/");
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
// ...
}
Это позволяет мне добавить заголовок Accept
, но когда я пытаюсь добавить Content-Type
, это вызывает следующее исключение:
Неправильное имя заголовка. Убедитесь, что заголовки запросов используются с
HttpRequestMessage
, заголовки ответов сHttpResponseMessage
и заголовки содержимого с объектамиHttpContent
.
Как настроить заголовок Content-Type
в запросе HttpClient
?
Тип содержимого - это заголовок содержимого, а не запроса, поэтому это не работает. AddWithoutValidation
, как предложил Роберт Леви, может работать, но вы также можете использовать тип содержимого при создании самого содержимого запроса (обратите внимание, что фрагмент кода добавляет "application/json" в двух местах - для заголовков Accept и Content-Type):
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
Encoding.UTF8,
"application/json");//CONTENT-TYPE header
client.SendAsync(request)
.ContinueWith(responseTask =>
{
Console.WriteLine("Response: {0}", responseTask.Result);
});
Response.Content.Headers
будет работать большую часть времени.
Response.Content.Headers
для ASP.Net Web API, также не сработали, но вы можете легко установить его, используя HttpContext.Current.Response.ContentType
если вам нужно.
Для тех, кто не видел комментария Джонса к решению carlos...
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Если вы не против небольшой зависимости от библиотеки, Flurl.Http [раскрытие: я автор] делает этот uber-simple, Его метод PostJsonAsync
обеспечивает как сериализацию содержимого, так и установку заголовка content-type
, а ReceiveJson
- десериализацию ответа. Если требуется заголовок accept
, вам нужно будет установить его самостоятельно, но Flurl также предоставляет довольно чистый способ:
using Flurl.Http;
var result = await "http://example.com/"
.WithHeader("Accept", "application/json")
.PostJsonAsync(new { ... })
.ReceiveJson<TResult>();
Flurl использует HttpClient и Json.NET под капотом, и это PCL, поэтому он будет работать на самых разных платформах.
PM> Install-Package Flurl.Http
PostUrlEncodedAsync
. tmenier.github.io/Flurl/fluent-http
попытайтесь использовать TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
Вызовите AddWithoutValidation
вместо Add
(см. эта ссылка MSDN).
В качестве альтернативы, я предполагаю, что API, который вы используете, действительно требует только этого для запросов POST или PUT (не обычные запросы GET). В этом случае, когда вы вызываете HttpClient.PostAsync
и передаете в HttpContent
, установите это в свойстве Headers
этого объекта HttpContent
.
.Net пытается заставить вас подчиняться определенным стандартам, а именно, что заголовок Content-Type
может быть указан только в запросах, имеющих контент (например, POST
, PUT
и т.д.). Поэтому, как указывали другие, предпочтительный способ установки заголовка Content-Type
заключается в свойстве HttpContent.Headers.ContentType
.
При этом некоторые API (например, LiquidFiles Api, начиная с 2016-12-19) требуют установки заголовка Content-Type
для запроса GET
..Net не позволит устанавливать этот заголовок на самом запросе - даже используя TryAddWithoutValidation
. Кроме того, вы не можете указать Content
для запроса - даже если он имеет нулевую длину. Единственный способ, которым я мог бы обойти это, - прибегнуть к размышлениям. Код (в случае, если ему это еще нужно) -
var field = typeof(System.Net.Http.Headers.HttpRequestHeaders)
.GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
?? typeof(System.Net.Http.Headers.HttpRequestHeaders)
.GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (field != null)
{
var invalidFields = (HashSet<string>)field.GetValue(null);
invalidFields.Remove("Content-Type");
}
_client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");
Edit:
Как отмечено в комментариях, это поле имеет разные имена в разных версиях dll. В исходном коде на GitHub поле в настоящее время называется s_invalidHeaders
. Пример был изменен для учета этого в предположении @David Thompson.
Дополнительная информация о .NET Core (после прочтения сообщения erdomke о настройке частного поля для предоставления типа содержимого в запросе, который не имеет содержимого)...
После отладки моего кода я не вижу, как частное поле устанавливается через отражение, поэтому я подумал, что попытаюсь воссоздать проблему.
Я пробовал следующий код, используя .Net 4.6:
HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, @"myUrl");
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
Task<HttpResponseMessage> response = client.SendAsync(httpRequest); //I know I should have used async/await here!
var result = response.Result;
И, как и ожидалось, я получаю сводное исключение с содержимым "Cannot send a content-body with this verb-type."
Однако, если я делаю то же самое с .NET Core (1.1) - , я не получаю исключение. Мой запрос был довольно счастливо ответил моим серверным приложением, а тип содержимого был поднят.
Я был приятно удивлен этим, и я надеюсь, что это поможет кому-то!