HttpClient: How to remove charset from Content-Type header
I was writing client library for one online service and faced situation where I had to remove charset definition from Content-Type header. It was like content type is application/json or response is 415 “Unsupported media type”. I was using HttpClient class to communicate with service and without additional efforts charset doesn’t go away. Here is how I got charset definition away from Content-Type header.
Problem
My problematic code was similar to one shown here.
var newUser = new { Id = 100, Name = "John Doe" };
var newUserJson = JsonConvert.SerializeObject(newUser);
var uri = new Uri("<address to some service>");
using (var client = new HttpClient())
using (var content = new StringContent(newUserJson, Encoding.UTF8, "application/json"))
{
var result = await client.PostAsync(uri, content);
// do something with result
}
Replacing in StringContent constructor Encoding.UTF8 with null doesn’t change the situation. In my case I got the following result when running my code.
Charset definition in request content type is the show stopper. To make things more interesting I bring out here all public constructors of StringContent class.
public StringContent(string content);
public StringContent(string content, Encoding encoding);
public StringContent(string content, Encoding encoding, string mediaType);
We either don’t specify content type and get text/plain as default or we specify content type with text encoding. There’s no way to specify just content type.
Why charset with content type?
I guess it comes down to SEO on performance strategies. There are online services like PageSpeed Insights and YSlow that suggest to give charset with textual content type so server on the other end doesn’t have to spend time on guessing correct charset. There’s one benefit more – if server knows charset before content starts then it doesn’t have to recode content that it parsed before charset HTML header.
Removing charset from content-type header
Solution is simple – we can create StringContent with whatever encoding and then set charset to empty string.
using (var client = new HttpClient())
using (var content = new StringContent(newUserJson, Encoding.UTF8, "application/json"))
{
content.Headers.ContentType.CharSet = "";
var result = await client.PostAsync(uri, content);
// do something with result
}
This removes charset definiton from request content type header. My code started work after this change.
Subclassing StringContent
Although my code worked I wasn’t happy with solution like this as I have to remember to set charset to empty string in every method I use HttpClient. I created new StringContentWithoutCharset class that extends StringContent.
public class StringContentWithoutCharset : StringContent
{
public StringContentWithoutCharset(string content) : base(content)
{
}
public StringContentWithoutCharset(string content, Encoding encoding) : base(content, encoding)
{
Headers.ContentType.CharSet = "";
}
public StringContentWithoutCharset(string content, Encoding encoding, string mediaType) : base(content, encoding, mediaType)
{
Headers.ContentType.CharSet = "";
}
public StringContentWithoutCharset(string content, string mediaType) : base(content, Encoding.UTF8, mediaType)
{
Headers.ContentType.CharSet = "";
}
}
This class adds additional constructor that accepts string content and content type. It calls base constructor with UTF8 charset but removes content type charset immediately. This is how my code looks like when using StringContentWithoutCharset class,
var newUser = new { Id = 100, Name = "John Doe" };
var newUserJson = JsonConvert.SerializeObject(newUser);
var uri = new Uri("<address to some service>");
using (var client = new HttpClient())
using (var content = new StringContentWithoutCharset(newUserJson, "application/json"))
{
var result = await client.PostAsync(uri, content);
// do something with result
}
This is the solution I am currently using,
Wrapping up
Although I don’t like to extend .NET Framework classes to add just one missing controller I didn’t had much chance this time. Still I’m okay with this solution as it hides the implementation of content-type charset removing from client code using the class. I hope the constructor I created makes its way to .NET Framework and .NET Core one day.
You could also achieve the same result by adding a DelegatingHandler to the HttpClient.
Then you could use any HttpContent, and it would be applied to all requests made using that client.
class ClearCharSetHandler : DelegatingHandler
{
protected override Task SendAsync(HttpRequestMessage request, CancellationToken token)
{
request.Content.Headers.ContentType.CharSet = “”;
return base.SendAsync(request, token);
}
}
Sheesh, took forever for me to figure this out. Thanks.
Good afternoon, I have a similar problem, I have exposed a service developed in .net to be consumed from a JAVA application, for some reason I do not know the owner of the application that makes the consumption is demanding that we remove the ContentType from the answer, but if I eliminate it when making the consumption it does not enter the method, everywhere they indicate that the adjustment must be made by the owner of the application that consumes the web services, but they indicated that they will not do it. I appreciate your comments on how to make this adjustment?
My code for consumption is:
Public Class FormatoRequerimiento
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
Public Shared Sub RequerimientoCapital()
Dim formato As New FormatoRequerimientoCapitalTO
formato = obtenerObjetoReqCapital()
HttpContext.Current.Response.Write(FormatoRequerimiento.getJSON(formato))
End Sub
Public Shared Function getJSON(ByVal formato As Object) As String
Return JsonConvert.SerializeObject(New With {.FormatoRequerimientoCapital = formato}, Newtonsoft.Json.Formatting.Indented)
End Function
Private Shared Function obtenerObjetoReqCapital() As FormatoRequerimientoCapitalTO
Dim formato As New FormatoRequerimientoCapitalTO
Dim resultado As New clsResultadosTO
Dim obUnidadCaptura As New UnidadCapturaTO
———————–
I found this annoying txt popup anytime I used the code editor within Godaddy. Some of these editors will break things. Thanks
I just faced the same problem. Thanks for sharing a solution.
Champion, thanks
Thanks a lot for sharing this.
dude! THANK YOU! Took us a while to figure this out!
Good stuff. Lost a couple of hours on this. Thanks for sharing.
You made my day…. many thanks for sharing!