Как да премахнете charset=utf8 от заглавката Content-Type, генерирана от HttpClient.PostAsJsonAsync()?

Имам проблем с HttpClient.PostAsJsonAsync()

В допълнение към "application/json" в заглавката "Content-Type" методът също добавя "charset=utf-8"

така че заглавката изглежда така:

Тип съдържание: приложение/json; charset=utf-8

Въпреки че ASP.NET WebAPI няма проблем с тази заглавка, открих, че други WebAPI, срещу които работя като клиент, не приемат заявка с тази заглавка, освен ако не е само приложение/json.

Има ли все пак да премахнете "charset=utf-8" от Content-Type, когато използвате PostAsJsonAsync(), или трябва да използвам друг метод?

РЕШЕНИЕ: Кредити за 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());
    }
}

person oronbz    schedule 18.04.2014    source източник
comment
още по-добро решение: stackoverflow.com/questions/40273638/   -  person Moe Howard    schedule 11.05.2018


Отговори (3)


Можете да извлечете от 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);
}
person Yishai Galatzer    schedule 18.04.2014
comment
Работи! Не мога да повярвам, че трябва да се върши толкова неудобна работа за това, но всъщност работи! Благодаря ти много - person oronbz; 19.04.2014
comment
@oronbz Сървърите трябва да приемат набора от знаци. Това, което трябва да направите, никога не трябва да бъде проблем. Като каза това, много по-лесно е да се справяте директно с HTTP, когато просто използвате производни класове HttpContent, вместо да зависи от форматиращи устройства. - person Darrel Miller; 19.04.2014
comment
@DarrelMiller Благодаря за коментара. трябва е безсмислена дума, когато сте клиент, а не сървър. Също така се заех с API, където те очакваха заглавка Accept в заявка POST, което няма никакъв смисъл и HttpClient не позволява вмъкване на заглавки Accept към POST, така че аз трябваше да използвам TryAddHeaderWithoutValidation(), за да го реша. Можете ли да предоставите пример за вашата препоръка? - person oronbz; 19.04.2014
comment
@oronbz Коментарът трябва да е оправдание защо няма лесен път за решаване на проблема ви. Не отхвърлях факта, че това е реален проблем, който трябва да бъде решен. Вижте другия ми отговор като предложение за това как можете да се справите с HTTP полезни натоварвания и да имате по-фина степен на контрол върху тях. - person Darrel Miller; 19.04.2014

За по-директен контрол върху полезния товар, който изпращате, можете да създадете производни класове 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.

Лично аз открих, че ми дава много по-добър контрол върху моите полезни товари.

person Darrel Miller    schedule 19.04.2014
comment
благодаря за това решение, приятелю, ще го използвам, когато имам нужда от този допълнителен контрол. - person oronbz; 20.04.2014

Харесвам отговора на Даръл повече от приетия, но все още беше твърде сложен за мен. Използвах това:

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

Излишно е да казвам, че можете да го адаптирате за всеки базов клас, който отговаря на вашите нужди. Надявам се това да помогне на някого.

person user1563125    schedule 01.04.2016
comment
Излишно е да казвам, че ако използвате този точен пример, вие сте сами за сериализация. - person user1563125; 01.04.2016