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

时间:2012-05-21 03:29:18

标签: c# asp.net api http rest

我正在尝试按照我呼叫的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-Type请求中设置HttpClient标头?

19 个答案:

答案 0 :(得分:751)

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。如Robert Levy所建议的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);
      });

答案 1 :(得分:125)

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

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

答案 2 :(得分:47)

如果你不介意一个小的图书馆依赖,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

答案 3 :(得分:29)

尝试使用TryAddWithoutValidation

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

答案 4 :(得分:21)

.Net试图强迫您遵守某些标准,即只能在包含内容的请求(例如Content-TypePOST等)上指定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");

修改

如评论中所述,此字段在dll的不同版本中具有不同的名称。在source code on GitHub中,该字段当前名为s_invalidHeaders。根据@David Thompson的建议,该示例已经过修改以解释此问题。

答案 5 :(得分:15)

致电AddWithoutValidation而非Add(请参阅this MSDN link)。

或者,我猜你正在使用的API实际上只需要POST或PUT请求(不是普通的GET请求)。在这种情况下,当您致电HttpClient.PostAsync并传入HttpContent时,请在该Headers对象的HttpContent媒体资源上进行设置。

答案 6 :(得分:14)

关于.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)做同样的事情 - 我没有例外。我的服务器应用程序和内容很愉快地回答了我的请求-type被选中了。

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

答案 7 :(得分:7)

对于那些为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");

答案 8 :(得分:3)

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 :(得分: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(...);
 }

答案 10 :(得分:0)

对于那些想要专门将 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)

答案 11 :(得分:0)

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

它对我有用。

答案 12 :(得分:0)

微软似乎试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置来做其他事情,这真是一种耻辱,尤其是考虑到这是一个客户端,而我们是由服务器端要求决定的,尤其是考虑到 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);
}

答案 13 :(得分:0)

尝试使用 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>();
            }
        }
    }
}

答案 14 :(得分:0)

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

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

答案 15 :(得分:0)

我最终遇到了类似的问题。 因此我发现,单击左上角的“代码”按钮时,软件PostMan可以生成代码。从中我们可以看到发生在“幕后”的情况,并且HTTP调用以许多代码语言生成。 curl命令,C#RestShart,java,nodeJs,...

这对我很有帮助,而不是使用.Net基本的HttpClient,我最终使用了RestSharp nuget包。

希望可以帮助别人!

答案 16 :(得分:0)

您需要这样做:

    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;

答案 17 :(得分:0)

您可以使用它,它将起作用!

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

答案 18 :(得分: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();