Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove charset=utf8 from Content-Type header generated by HttpClient.PostAsJsonAsync()?

I have an issue with HttpClient.PostAsJsonAsync()

In addition to "application/json" in the "Content-Type" header the method also adds "charset=utf-8"

so the header looks like this:

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

While ASP.NET WebAPI doesn't have any issue with this header, i've found that other WebAPIs I work against as a client don't accept request with this header, unless it's only application/json.

Is there anyway to remove the "charset=utf-8" from Content-Type when using PostAsJsonAsync(), or should I use another method?

SOLUTION: Credits to Yishai!

using System.Net.Http.Headers;

public class NoCharSetJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
   public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
   {
       base.SetDefaultContentHeaders(type, headers, mediaType);
       headers.ContentType.CharSet = "";
   }
}

public static class HttpClientExtensions
{
    public static async Task<HttpResponseMessage> PostAsJsonWithNoCharSetAsync<T>(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken)
    {
        return await client.PostAsync(requestUri, value, new NoCharSetJsonMediaTypeFormatter(), cancellationToken);
    }

    public static async Task<HttpResponseMessage> PostAsJsonWithNoCharSetAsync<T>(this HttpClient client, string requestUri, T value)
    {
        return await client.PostAsync(requestUri, value, new NoCharSetJsonMediaTypeFormatter());
    }
}
like image 619
oronbz Avatar asked Apr 18 '14 19:04

oronbz


2 Answers

For more direct control over the payload you send, you can create derived HttpContent classes instead of letting your object be passed to an ObjectContent class which then delegates streaming to a Formatter class.

A JsonContent class that supports both reading and writing looks like this,

public class JsonContent : HttpContent
{
    private readonly Stream _inboundStream;
    private readonly JToken _value;

    public JsonContent(JToken value)
    {
        _value = value;
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
    }

    public JsonContent(Stream inboundStream)
    {
        _inboundStream = inboundStream;
    }

    public async Task<JToken> ReadAsJTokenAsync()
    {
        return _value ?? JToken.Parse(await ReadAsStringAsync());
    }

    protected async override Task<Stream> CreateContentReadStreamAsync()
    {
        return _inboundStream;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        if (_value != null)
        {
            var jw = new JsonTextWriter(new StreamWriter(stream)) {Formatting = Formatting.Indented};
            _value.WriteTo(jw);
            jw.Flush();
        } else if (_inboundStream != null)
        {
            return _inboundStream.CopyToAsync(stream);
        }
        return Task.FromResult<object>(null);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _inboundStream.Dispose();   
        }
        base.Dispose(disposing);
    }
}

Once you have this class you can then do,

var content = new JsonContent(myObject);
_httpClient.PostAsync(uri,content);

If you need to change any of the content Headers you can do that manually before sending the request. And if you need to mess with any of the request headers then you use the SendAsync overload,

var content = new JsonContent(myObject);
// Update Content headers here
var request = new HttpRequestMessage {RequestUri = uri, Content = content };
// Update request headers here
_httpClient.SendAsync(request);

Derived content classes are easy to create for pretty much any media type or any source of data. I've created all kinds of classes derived from HttpContent. e.g. FileContent, EmbeddedResourceContent, CSVContent, XmlContent, ImageContent, HalContent, CollectionJsonContent, HomeContent, ProblemContent.

Personally, I've found it gives me much better control over my payloads.

like image 185
Darrel Miller Avatar answered Oct 24 '22 18:10

Darrel Miller


You can derive from JsonMediaTypeFormatter and override SetDefaultContentHeaders.

Call base.SetDefaultContentHeaders() and then clear headers.ContentType.CharSet

then write your own extension method based on the following code:

public static Task<HttpResponseMessage> PostAsJsonAsync<T>(this HttpClient client, string requestUri, T value, CancellationToken cancellationToken)
{
    return client.PostAsync(requestUri, value, 
            new JsonMediaTypeFormatter(), cancellationToken);
}

In essence something like:

public static Task<HttpResponseMessage> PostAsJsonWithNoCharSetAsync<T>(this HttpClient client, string requestUri, T value, CancellatioNToken cancellationToken)
{
    return client.PostAsync(requestUri, value, 
          new NoCharSetJsonMediaTypeFormatter(), cancellationToken);
}
like image 32
Yishai Galatzer Avatar answered Oct 24 '22 20:10

Yishai Galatzer