How do you set the Content-Type header for an Http

2018-12-31 14:49发布

问题:

I\'m trying to set the Content-Type header of an HttpClient object as required by an API I am calling.

I tried setting the Content-Type like below:

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\");
    // ...
}

It allows me to add the Accept header but when I try to add Content-Type it throws the following exception:

Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.

How can I set the Content-Type header in a HttpClient request?

回答1:

The content type is a header of the content, not of the request, which is why this is failing. AddWithoutValidation as suggested by Robert Levy may work, but you can also set the content type when creating the request content itself (note that the code snippet adds \"application/json\" in two places-for Accept and Content-Type headers):

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:

For those who didn\'t see Johns comment to carlos solution ...

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


回答3:

If you don\'t mind a small library dependency, Flurl.Http [disclosure: I\'m the author] makes this uber-simple. Its PostJsonAsync method takes care of both serializing the content and setting the content-type header, and ReceiveJson deserializes the response. If the accept header is required you\'ll need to set that yourself, but Flurl provides a pretty clean way to do that too:

using Flurl.Http;

var result = await \"http://example.com/\"
    .WithHeader(\"Accept\", \"application/json\")
    .PostJsonAsync(new { ... })
    .ReceiveJson<TResult>();

Flurl uses HttpClient and Json.NET under the hood, and it\'s a PCL so it\'ll work on a variety of platforms.

PM> Install-Package Flurl.Http


回答4:

try to use TryAddWithoutValidation

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


回答5:

.Net tries to force you to obey certain standards, namely that the Content-Type header can only be specified on requests that have content (e.g. POST, PUT, etc.). Therefore, as others have indicated, the preferred way to set the Content-Type header is through the HttpContent.Headers.ContentType property.

With that said, certain APIs (such as the LiquidFiles Api, as of 2016-12-19) requires setting the Content-Type header for a GET request. .Net will not allow setting this header on the request itself -- even using TryAddWithoutValidation. Furthermore, you cannot specify a Content for the request -- even if it is of zero-length. The only way I could seem to get around this was to resort to reflection. The code (in case some else needs it) is

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:

As noted in the comments, this field has different names in different versions of the dll. In the source code on GitHub, the field is currently named s_invalidHeaders. The example has been modified to account for this per the suggestion of @David Thompson.



回答6:

Call AddWithoutValidation instead of Add (see this MSDN link).

Alternatively, I\'m guessing the API you are using really only requires this for POST or PUT requests (not ordinary GET requests). In that case, when you call HttpClient.PostAsync and pass in an HttpContent, set this on the Headers property of that HttpContent object.



回答7:

Some extra information about .NET Core (after reading erdomke\'s post about setting a private field to supply the content-type on a request that doesn\'t have content)...

After debugging my code, I can\'t see the private field to set via reflection - so I thought I\'d try to recreate the problem.

I have tried the following code using .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;

And, as expected, I get an aggregate exception with the content \"Cannot send a content-body with this verb-type.\"

However, if i do the same thing with .NET Core (1.1) - I don\'t get an exception. My request was quite happily answered by my server application, and the content-type was picked up.

I was pleasantly surprised about that, and I hope it helps someone!



回答8:

Ok, it\'s not HTTPClient but if u can use it, WebClient is quite easy:

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


回答9:

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

It\'s all what you need.

With using Newtonsoft.Json, if you need a content as json string.

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