972

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

我尝试设置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-TypeHttpClient请求中设置标头?

4

20 回答 20

1213

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。AddWithoutValidation正如 Robert Levy 所建议的那样可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码片段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);
      });
于 2012-05-21T03:54:34.030 回答
201

对于那些没有看到约翰对卡洛斯解决方案发表评论的人......

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
于 2013-06-06T10:15:17.987 回答
60

如果您不介意小型库依赖项,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
于 2014-06-22T15:19:50.010 回答
42

尝试使用 TryAddWithoutValidation

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
于 2015-11-19T22:48:39.167 回答
41

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

话虽如此,某些 API(例如LiquidFiles Api,截至 2016 年 12 月 19 日)需要设置请求的Content-Type标头。GET.Net 不允许在请求本身上设置此标头——即使使用TryAddWithoutValidation. 此外,您不能Content为请求指定 a ——即使它的长度为零。我似乎可以解决这个问题的唯一方法是诉诸反思。代码(以防其他人需要它)是

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 的建议,该示例已被修改以解决此问题。

于 2016-12-19T21:32:04.923 回答
29

对于那些烦恼的人charset

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

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

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

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

编辑

@DesertFoxAZ 建议也可以使用以下代码并且可以正常工作。(我自己没有测试,如果它的工作率和评论他的功劳)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
于 2019-08-18T07:19:38.630 回答
23

有关 .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) 做同样的事情 -我不会遇到异常。我的服务器应用程序很高兴地回答了我的请求,并且获取了内容类型。

我对此感到惊喜,我希望它对某人有所帮助!

于 2017-06-12T08:48:20.180 回答
18

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

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

于 2012-05-21T03:32:53.207 回答
4
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();
    }
}
于 2018-05-28T13:56:52.693 回答
3

你可以用这个它会工作!

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();
于 2019-07-10T04:01:03.970 回答
3

诀窍是您可以设置各种标题,例如:

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

但没有任何标题。例如:

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

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

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

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

在将正文添加到 http 请求的 Content 部分时,您必须声明正文的编码和类型:

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

只有这样,适用的 http 标头才会自动添加到请求中:

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

It was hard to find this out, with Fiddler, on a machine without a proxy server. Visual Studio used to have a Network Tool where you could inspect all headers, but only in version 2015, not in newer versions 2017 or 2022. If you use the debugger to inspect request.Headers, you will not find the header added automagically by StringContent().

于 2022-01-05T13:20:02.960 回答
2

好的,它不是 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(...);
 }
于 2018-07-09T20:21:33.207 回答
2

似乎微软试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置来做其他事情,这真的很可惜,特别是考虑到这是一个客户端,我们受服务器端要求的支配,特别是考虑到Microsoft 服务器端框架本身需要它!

因此,基本上微软在连接到强迫我们养成不良习惯的服务器技术时,试图强迫我们养成良好的习惯……

如果 Microsoft 的任何人正在阅读此内容,请修复它...

无论哪种方式,对于需要 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);
}
于 2021-03-03T02:12:40.967 回答
1

你需要这样做:

    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;
于 2019-08-09T18:55:33.937 回答
1

尝试使用 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>();
            }
        }
    }
}
于 2021-02-28T22:17:17.623 回答
1

我得到了 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");
        
            ...
        }

它对我有用。

于 2021-04-20T19:45:52.737 回答
1

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

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

这里的优点是代码更简洁,您可以避免字符串化的 json。更多详细信息,请访问:https://docs.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)

于 2021-07-18T14:01:49.553 回答
0

我发现通过以下方式最简单易懂:

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();
于 2019-06-11T22:10:01.057 回答
0

我最终遇到了类似的问题。所以我发现Software PostMan可以在点击左上角的“代码”按钮时生成代码。从中我们可以看到“幕后”发生的事情,并且 HTTP 调用是用多种代码语言生成的;curl 命令,C# RestShat,java,nodeJs,...

这对我有很大帮助,我最终没有使用.Net base HttpClient,而是使用了 RestSharp nuget 包。

希望可以帮助别人!

于 2020-09-18T16:16:24.127 回答
-3

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

是的!...这解决了 ATS REST API 的问题:SharedKey 现在可以工作了! https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628

于 2020-11-26T10:13:24.570 回答