【问题标题】:How to remove charset=utf8 from Content-Type header generated by HttpClient.PostAsJsonAsync()?如何从 HttpClient.PostAsJsonAsync() 生成的 Content-Type 标头中删除 charset=utf8?
【发布时间】:2014-04-18 19:41:41
【问题描述】:

我有一个问题 HttpClient.PostAsJsonAsync()

除了“Content-Type”标头中的“application/json”之外,该方法还添加了“charset=utf-8”

所以标题看起来像这样:

内容类型:应用程序/json;字符集=utf-8

虽然 ASP.NET WebAPI 对此标头没有任何问题,但我发现我作为客户端使用的其他 WebAPI 不接受带有此标头的请求,除非它只是 application/json。

在使用 PostAsJsonAsync() 时,是否可以从 Content-Type 中删除“charset=utf-8”,还是应该使用其他方法?

解决方案: 感谢 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());
    }
}

【问题讨论】:

标签: asp.net json utf-8 asp.net-web-api httpclient


【解决方案1】:

您可以从 JsonMediaTypeFormatter 派生并覆盖 SetDefaultContentHeaders。

致电base.SetDefaultContentHeaders(),然后清除headers.ContentType.CharSet

然后根据以下代码编写自己的扩展方法:

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

本质上是这样的:

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

【讨论】:

  • 有效!我不敢相信必须为此做如此尴尬的工作,但它确实有效!非常感谢
  • @oronbz 服务器应该接受字符集。您必须做的事情永远不会成为问题。话虽如此,当您只使用派生的 HttpContent 类而不是依赖于格式化程序时,直接处理 HTTP 要容易得多。
  • @DarrelMiller 感谢您的评论。当您是客户端而不是服务器时,“应该” 是一个没有意义的词。我还处理了一个 API,他们期望 "Accept" 标头在 POST 请求中完全没有意义,并且 HttpClient 不允许将 Accept 标头插入到 POST 所以我不得不使用 TryAddHeaderWithoutValidation() 来解决它。您能否提供一个推荐示例?
  • @oronbz “应该”评论是对为什么没有简单的方法来解决您的问题的理由。我并没有否认这是一个需要处理的真正问题。请参阅我的其他答案,作为有关如何处理 HTTP 有效负载并更好地控制它们的建议。
【解决方案2】:

为了更直接地控制您发送的有效负载,您可以创建派生的 HttpContent 类,而不是将您的对象传递给 ObjectContent 类,该类然后将流式传输委托给 Formatter 类。

一个同时支持读写的JsonContent类长这样,

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

一旦你有了这门课,你就可以做,

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

如果您需要更改任何内容标头,您可以在发送请求之前手动执行此操作。如果您需要弄乱任何请求标头,那么您可以使用 SendAsync 重载,

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

几乎可以为任何媒体类型或任何数据源创建派生内容类。我创建了从 HttpContent 派生的各种类。例如FileContent、EmbeddedResourceContent、CSVContent、XmlContent、ImageContent、HalContent、CollectionJsonContent、HomeContent、ProblemContent。

就我个人而言,我发现它可以让我更好地控制我的有效载荷。

【讨论】:

  • 感谢这个解决方案伙伴,我会在需要额外控制时使用它。
【解决方案3】:

我更喜欢 Darrel 的回答,而不是接受的回答,但它对我来说仍然太复杂。我用这个:

public class ContentTypeSpecificStringContent : StringContent
{
    /// <summary>
    /// Ensure content type is reset after base class mucks it up.
    /// </summary>
    /// <param name="content">Content to send</param>
    /// <param name="encoding">Encoding to use</param>
    /// <param name="contentType">Content type to use</param>
    public ContentTypeSpecificStringContent(string content, Encoding encoding, string contentType)
        : base(content, encoding, contentType)
    {
        Headers.ContentType = new MediaTypeHeaderValue(contentType);
    }
}

不用说,您可以将其调整为适合您需要的任何基类。希望对某人有所帮助。

【讨论】:

  • 不用说,如果你使用这个确切的例子,你就需要自己进行序列化了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 2018-03-12
  • 2017-10-08
  • 2016-04-27
  • 1970-01-01
相关资源
最近更新 更多