Явно задайте заголовки Content-Type для получения работы в HttpClient

Есть ли способ, в котором я могу явно установить значения заголовка Content-Type при выполнении GET с HttpClient?

Я понимаю этот протокол breaks 1.1, но я работаю с API, который не соответствует ему, и НЕОБХОДИМО установить набор заголовков Content-Type.

Я пробовал это безрезультатно...

using (var httpClient = new HttpClient())
{
   var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.com");

   httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded+v1.3");

   await httpClient.SendAsync(httpRequestMessage)
}

Я проверил DefaultRequestHeaders после добавления TryAddWithoutValidation и, похоже, не устанавливает значение Content-Type.

Если я попытаюсь установить Content-Type httpRequestMessage (установив httpRequestMessage.Content = ..., я получаю следующую ошибку:

Cannot send a content-body with this verb-type.

Есть ли способ, который я могу явно установить Content-Type для операции GET с помощью HttpClient?

Ответ 1

Основываясь на моих выводах, я пришел к выводу, что HttpClient является очень ограничительным с точки зрения правил протокола. Я также нашел отражение в DLL реализации, и я не мог найти ничего, что могло бы указывать на то, что оно допускает нарушения протокола.

Запросы GET не должны содержать заголовки типа контента, и HttpClient применяет это правило.

Я думаю, что сообщение об исключении, когда вы пытаетесь установить заголовок типа контента, самоописательно:

System.InvalidOperationException: Неправильное имя заголовка. Убедитесь, что заголовки запросов используются с HttpRequestMessage, заголовками ответов с HttpResponseMessage и заголовками содержимого с объектами HttpContent.

Также, если вы используете настройку тела контента, вы получите еще одно самоописательное сообщение:

System.Net.ProtocolViolationException: Не удается отправить контент-тело с помощью этого типа.

Поскольку вы готовы нарушать правила HTTP для запросов GET, я уверен, что ваш единственный вариант - придерживаться менее ограничительного WebClient, который работает в этом сценарии.

Ответ 2

Возможно - и очень грязно - переопределить поведение библиотеки с небольшим количеством размышлений и введя DelegatingHandler, который вы даете в качестве аргумента конструктору HttpClient. Смотрите код ниже.

public class HmacAuthenticatingHandler : DelegatingHandler
{
    public HmacAuthenticatingHandler(HttpMessageHandler innerHandler) 
       : base(innerHandler)
    {
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {

        // HACK: Set 'Content-Type' even for GET requests
        var invalidHeaders = (HashSet<string>)typeof(HttpHeaders)
            // use "_invalidHeaders" for System.Net.Http v2.2+
            .GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(request.Headers);
        invalidHeaders.Remove("Content-Type");

        request.Headers.Remove("Content-Type");
        request.Headers.Add("Content-Type", "application/json");

        var response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}

Ответ 4

Вы пытались добавить заголовки в заголовок содержимого (как указано в заголовке запроса) см. здесь