Как установить заголовок Content-Type для запроса HttpClient?
Я пытаюсь установить заголовок 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
?
Ответы
Ответ 1
Тип содержимого - это заголовок содержимого, а не запроса, поэтому это не удается. 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);
});
Ответ 2
Для тех, кто не видел комментария Джонса к решению carlos...
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Ответ 3
Если вы не возражаете против небольшой зависимости от библиотеки, Flurl.Http [раскрытие: я автор] делает это очень просто. Его метод 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
Ответ 4
попытайтесь использовать TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
Ответ 5
.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.
Ответ 6
Дополнительная информация о .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) - , я не получаю исключение. Мой запрос был довольно счастливо ответил моим серверным приложением, а тип содержимого был поднят.
Я был приятно удивлен этим, и я надеюсь, что это поможет кому-то!
Ответ 7
Вызовите AddWithoutValidation
вместо Add
(см. эта ссылка MSDN).
В качестве альтернативы, я предполагаю, что API, который вы используете, действительно требует только этого для запросов POST или PUT (не обычные запросы GET). В этом случае, когда вы вызываете HttpClient.PostAsync
и передаете в HttpContent
, установите это в свойстве Headers
этого объекта HttpContent
.
Ответ 8
var content = new JsonContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8"));
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("IEEE754Compatible", "true"));
Это все, что вам нужно.
С использованием Newtonsoft.Json, если вам нужен контент в виде строки json.
public class JsonContent : HttpContent
{
private readonly MemoryStream _stream = new MemoryStream();
~JsonContent()
{
_stream.Dispose();
}
public JsonContent(object value)
{
Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var contexStream = new MemoryStream())
using (var jw = new JsonTextWriter(new StreamWriter(contexStream)) { Formatting = Formatting.Indented })
{
var serializer = new JsonSerializer();
serializer.Serialize(jw, value);
jw.Flush();
contexStream.Position = 0;
contexStream.WriteTo(_stream);
}
_stream.Position = 0;
}
private JsonContent(string content)
{
Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var contexStream = new MemoryStream())
using (var sw = new StreamWriter(contexStream))
{
sw.Write(content);
sw.Flush();
contexStream.Position = 0;
contexStream.WriteTo(_stream);
}
_stream.Position = 0;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return _stream.CopyToAsync(stream);
}
protected override bool TryComputeLength(out long length)
{
length = _stream.Length;
return true;
}
public static HttpContent FromFile(string filepath)
{
var content = File.ReadAllText(filepath);
return new JsonContent(content);
}
public string ToJsonString()
{
return Encoding.ASCII.GetString(_stream.GetBuffer(), 0, _stream.GetBuffer().Length).Trim();
}
}
Ответ 9
Для тех, кто беспокоился о charset
У меня был особый случай, когда поставщик услуг не принимал кодировку, и они отказывались изменять подструктуру, чтобы позволить это...
К сожалению, HttpClient устанавливал заголовок автоматически через StringContent, и независимо от того, передали ли вы null или Encoding.UTF8, он всегда будет устанавливать кодировку...
Сегодня я был на грани изменения подсистемы; переходя от HttpClient к чему-то еще, что-то пришло мне в голову... почему бы не использовать отражение, чтобы очистить "кодировку"?...
И прежде чем я попробовал это, я подумал о способе "возможно я смогу изменить его после инициализации", и это сработало.
Вот как вы можете установить точный заголовок "application/json" без "; charset = utf-8".
var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to string
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
stringContent.Headers.ContentType.CharSet = null;
return stringContent;
Примечание: Значение null
в следующем не будет работать, и добавьте "; charset = utf-8"
return new StringContent(jsonRequest, null, "application/json");
Ответ 10
Хорошо, это не HTTPClient, но если вы можете использовать его, WebClient довольно прост:
using (var client = new System.Net.WebClient())
{
client.Headers.Add("Accept", "application/json");
client.Headers.Add("Content-Type", "application/json; charset=utf-8");
client.DownloadString(...);
}
Ответ 11
Вы можете использовать это, это будет работать!
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get,"URL");
msg.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.SendAsync(msg);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
Ответ 12
Вам нужно сделать это так:
HttpContent httpContent = new StringContent(@"{ the json string }");
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage message = client.PostAsync(@"{url}", httpContent).Result;
Ответ 13
Я считаю, что это наиболее просто и легко понять следующим образом:
async Task SendPostRequest()
{
HttpClient client = new HttpClient();
var requestContent = new StringContent(<content>);
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync(<url>, requestContent);
var responseString = await response.Content.ReadAsStringAsync();
}
...
SendPostRequest().Wait();