如何从 HttpClient.PostAsJsonAsync() 生成的 Content-Type 标头中删除 charset=utf8?

Posted

技术标签:

【中文标题】如何从 HttpClient.PostAsJsonAsync() 生成的 Content-Type 标头中删除 charset=utf8?【英文标题】:How to remove charset=utf8 from Content-Type header generated by HttpClient.PostAsJsonAsync()? 【发布时间】: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());
    

【问题讨论】:

更多更好的解决方案:***.com/questions/40273638/… 【参考方案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);
    

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

【讨论】:

不用说,如果你使用这个确切的例子,你就需要自己进行序列化了。

以上是关于如何从 HttpClient.PostAsJsonAsync() 生成的 Content-Type 标头中删除 charset=utf8?的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从回收器适配器发送到片段 |如何从 recyclerview 适配器调用片段函数

如何从 Firebase 获取所有设备令牌?

如何直接从类调用从接口继承的方法?

如何从服务器获取和设置 android 中的 API(从服务器获取 int 值)?如何绑定和实现这个

如何从Mac从android studio中的fabric注销? [复制]

如何从设备中获取 PDF 文件以便能够从我的应用程序中上传?