如何为HttpClient请求设置Content-Type标头?

我正在尝试根据我正在调用的API的要求设置HttpClient对象的Content-Type标头。

我尝试设置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对象的内容标头。

如何在HttpClient请求中设置Content-Type标头?

1218078 次浏览

调用AddWithoutValidation而不是Add(参见此MSDN链接)。

或者,我猜你使用的API真的只需要POST或PUT请求(不是普通的GET请求)。在这种情况下,当你调用HttpClient.PostAsync并传入HttpContent时,在该HttpContent对象的Headers属性上设置它。

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。#0由Robert Levy推荐可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码片段在两个地方添加了application/json-接受和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);});

对于那些没有看到Johns评论Carlos Solutions的人…

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

如果你不介意一个小的库依赖项,跳转[披露:我是作者]让这变得超级简单。它的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

尝试使用TryAddWithotVal的

  var client = new HttpClient();client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");

. net试图强迫您遵守某些标准,即Content-Type标头只能在具有内容的请求(例如POSTPUT等)上指定。因此,正如其他人所指出的,设置Content-Type标头的首选方法是通过#4属性。

话虽如此,某些API(例如LiquidFiles接口,截至2016-12-19)需要为GET请求设置Content-Type标头。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");

编辑:

如注释中所述,该字段在不同版本的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)-我没有得到一个例外。做同样的事情,我的服务器应用程序非常高兴地回答了我的请求,并且选择了内容类型。

我对此感到惊喜,我希望它能帮助别人!

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

好吧,它不是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(...);}

我觉得最简单和容易理解的方式如下:

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();

你可以使用它,这将是工作!

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();

你需要这样做:

    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;

对于那些谁困扰charset

我有一个非常特殊的情况,即服务提供者不接受字符集,他们拒绝更改子结构以允许它…不幸的是,HttpClient通过StringContent自动设置标头,无论您传递null还是Encoding.UTF8,它总是会设置字符集…

今天我在改变子系统的边缘;从HttpClient移动到其他任何东西,我想到了一些东西…,为什么不使用反射来清空“字符集”?…在我尝试之前,我想到了一个方法,“也许我可以在初始化后更改它”,并且有效。

以下是如何在没有“; charset=utf-8”的情况下设置确切的“应用程序/json”标头。

var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to stringvar 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");

编辑

@DesertFoxAZ建议也可以使用以下代码并且运行良好。(我自己没有测试过,如果它的工作率和在评论中归功于他)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

我也遇到了类似的问题。所以我发现软件PostMan可以在点击左上角的“代码”按钮时生成代码。从中我们可以看到“幕后”发生了什么,HTTP调用是以多种代码语言生成的;curl命令、C#RepShart、java、nodeJs、…

这对我帮助很大,而不是使用。Net base HttpClient我最终使用了RESTSharp nuget包。

希望能帮助到别人!

stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

🎉耶!🎉解决了ATS REST API的问题:SharedKey现在起作用了!😄👍🍻

来源:https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628

尝试使用HttpClientFactory

services.AddSingleton<WebRequestXXX>().AddHttpClient<WebRequestXXX>("ClientX", config =>{config.BaseAddress = new System.Uri("https://jsonplaceholder.typicode.com");config.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));config.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");});

======================

public class WebRequestXXXX{private readonly IHttpClientFactory _httpClientFactory;
public WebRequestXXXX(IHttpClientFactory httpClientFactory){_httpClientFactory = httpClientFactory;}
public List<Posts> GetAllPosts(){using (var _client = _httpClientFactory.CreateClient("ClientX")){var response = _client.GetAsync("/posts").Result;
if (response.IsSuccessStatusCode){var itemString = response.Content.ReadAsStringAsync().Result;var itemJson = System.Text.Json.JsonSerializer.Deserialize<List<Posts>>(itemString,new System.Text.Json.JsonSerializerOptions{PropertyNameCaseInsensitive = true});
return itemJson;}else{return new List<Posts>();}}}}

看起来微软试图强迫开发人员遵循他们的标准,甚至没有给出任何选项或设置来做其他事情,这真的是一种耻辱,特别是考虑到这是一个客户端,我们是由服务器端要求决定的,特别是考虑到微软服务器端框架本身需要它!

所以基本上微软试图强迫我们在连接到他们的服务器技术时养成良好的习惯,这迫使我们养成不好的习惯。

如果微软的任何人正在阅读这篇文章,请修复它…

无论哪种方式,对于任何需要Get等的内容类型标头的人,在较旧的. Net版本中,都可以在https://stackoverflow.com/a/41231353/640195处使用@erdomke的答案,不幸的是,这在较新的. Net核心版本中不再有效。

以下代码已经过测试,可以与. Net core 3.1一起使用,从GitHub上的源代码来看,它应该也可以与较新的. Net版本一起使用。

private void FixContentTypeHeaders(){var assembly = typeof(System.Net.Http.Headers.HttpRequestHeaders).Assembly;var assemblyTypes = assembly.GetTypes();
var knownHeaderType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeader");var headerTypeField = knownHeaderType?.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FirstOrDefault(n => n.Name.Contains("HeaderType"));if (headerTypeField is null) return;
var headerTypeFieldType = headerTypeField.FieldType;var newValue = Enum.Parse(headerTypeFieldType, "All");
var knownHeadersType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeaders");var contentTypeObj = knownHeadersType.GetFields().FirstOrDefault(n => n.Name == "ContentType").GetValue(null);
if (contentTypeObj is null) return;
headerTypeField.SetValue(contentTypeObj, newValue);}

我得到了RestSharp的答案:

        private async Task<string> GetAccessTokenAsync(){var client = new RestClient(_baseURL);
var request = new RestRequest("auth/v1/login", Method.POST, DataFormat.Json);
request.AddHeader("Content-Type", "application/json");request.AddHeader("x-api-key", _apiKey);request.AddHeader("Accept-Language", "br");request.AddHeader("x-client-tenant", "1");        
...}

它为我工作。

对于那些想要将Content-Type专门设置为Json的人,您可以使用扩展方法PostAsJsonAsync。

using System.Net.Http.Json; //this is needed for PostAsJsonAsync to work//....HttpClient client = new HttpClient();HttpResponseMessage response = awaitclient.PostAsJsonAsync("http://example.com/" + "relativeAddress",new{name = "John Doe",age = 33});//Do what you need to do with your response

这里的优点是代码更清晰,您可以避免字符串化的json。更多详细信息可以在以下位置找到:https://learn.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)

把戏是您可以设置各种标题,例如:

HttpRequestMessage request = new HttpRequestMessage();request.Headers.Add("Accept-Language", "en"); //works OK

但不是任何标题。例如:

request.Headers.Add("Content-Type", "application/json");//wrong

将引发运行时异常Misused header name。这似乎可以工作:

request.Headers.Add(HttpRequestHeader.ContentType.ToString(), //useless"application/json");

但这给出了一个名为ContentType的无用标头,没有连字符。标头名称不区分大小写,但对连字符非常敏感。

的解决方案是在将主体添加到超文本传输协议请求的内容部分时声明主体的编码和类型:

string Body = "...";request.Content =new StringContent(Body, Encoding.UTF8, "application/json");

只有这样,适用的超文本传输协议标头才会自动添加到请求中:

Content-Type: application/json; charset=utf-8

使用Fiddler在没有代理服务器的机器上很难发现这一点。Visual Studio曾经有一个网络工具,你可以在其中检查所有标头,但仅限于2015版本,而不是2017或2022的较新版本。如果你使用调试器检查request.Headers,你将找不到StringContent()自动添加的标头。

Api回来了

“不支持的媒体类型”,“状态”: 415

将ContentType添加到jsonstring确实很神奇,这是我的脚本到今天为止100%工作

    using (var client = new HttpClient()){var endpoint = "api/endpoint;var userName = "xxxxxxxxxx";var passwd = "xxxxxxxxxx";var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");
client.BaseAddress = new Uri("https://example.com/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));            
HttpResponseMessage response = await client.PostAsync(endpoint, content);
if (response.IsSuccessStatusCode){// Get the URI of the created resource.Uri returnUrl = response.Headers.Location;Console.WriteLine(returnUrl);}string responseBody = await response.Content.ReadAsStringAsync();return responseBody;}