Как извлечь значение настраиваемого заголовка в обработчик сообщений Web API?

В настоящее время у меня есть обработчик сообщений в моей службе веб-API, который переопределяет "SendAsync" следующим образом:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  //implementation
}

Внутри этого кода мне нужно проверить значение заголовка пользовательского добавленного запроса с именем MyCustomID. Проблема в том, что я делаю следующее:

if (request.Headers.Contains("MyCustomID"))  //OK
    var id = request.Headers["MyCustomID"];  //build error - not OK

... Появляется следующее сообщение об ошибке:

Невозможно применить индексирование с [] к выражению типа 'System.Net.Http.Headers.HttpRequestHeaders'

Как я могу получить доступ к отдельному заголовку пользовательского запроса с помощью HttpRequestMessage (Документация MSDN), который передается в этот переопределенный метод?

Ответ 1

Попробуйте что-то вроде этого:

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
var id = headerValues.FirstOrDefault();

Также существует метод TryGetValues ​​в заголовках, которые вы можете использовать, если у вас не всегда гарантирован доступ к заголовку.

Ответ 2

Строка ниже throws exception, если ключ не существует.

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");

Работать вокруг:

Включить System.Linq;

IEnumerable<string> headerValues;
var userId = string.Empty;

     if (request.Headers.TryGetValues("MyCustomID", out headerValues))
     {
         userId = headerValues.FirstOrDefault();
     }           

Ответ 3

Чтобы расширить ответ на Youssef, я написал этот метод, основанный на Drew, о том, что заголовок не существует, потому что я столкнулся с этой ситуацией во время модульного тестирования.

private T GetFirstHeaderValueOrDefault<T>(string headerKey, 
   Func<HttpRequestMessage, string> defaultValue, 
   Func<string,T> valueTransform)
    {
        IEnumerable<string> headerValues;
        HttpRequestMessage message = Request ?? new HttpRequestMessage();
        if (!message.Headers.TryGetValues(headerKey, out headerValues))
            return valueTransform(defaultValue(message));
        string firstHeaderValue = headerValues.FirstOrDefault() ?? defaultValue(message);
        return valueTransform(firstHeaderValue);
    }

Здесь пример использования:

GetFirstHeaderValueOrDefault("X-MyGuid", h => Guid.NewGuid().ToString(), Guid.Parse);

Также посмотрите на ответ @doguhan-uluca для более общего решения.

Ответ 4

Создайте новый метод - Возвращает индивидуальное значение заголовка HTTP "и каждый раз вызывайте этот метод с ключевым значением, когда вам нужно получить доступ к нескольким ключевым значениям из HttpRequestMessage.

public static string GetHeader(this HttpRequestMessage request, string key)
        {
            IEnumerable<string> keys = null;
            if (!request.Headers.TryGetValues(key, out keys))
                return null;

            return keys.First();
        }

Ответ 5

Для дальнейшего расширения решения @neontapir, здесь более общее решение, которое может применяться к HttpRequestMessage или HttpResponseMessage одинаково и не требует ручных кодированных выражений или функций.

using System.Net.Http;
using System.Collections.Generic;
using System.Linq;

public static class HttpResponseMessageExtensions
{
    public static T GetFirstHeaderValueOrDefault<T>(
        this HttpResponseMessage response,
        string headerKey)
    {
        var toReturn = default(T);

        IEnumerable<string> headerValues;

        if (response.Content.Headers.TryGetValues(headerKey, out headerValues))
        {
            var valueString = headerValues.FirstOrDefault();
            if (valueString != null)
            {
                return (T)Convert.ChangeType(valueString, typeof(T));
            }
        }

        return toReturn;
    }
}

Использование образца:

var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");

Ответ 6

Для ASP.Net Core существует простое решение, если вы хотите использовать параметр непосредственно в методе контроллера: используйте аннотацию [FromHeader].

        public JsonResult SendAsync([FromHeader] string myParam)
        {
        if(myParam == null)  //Param not set in request header
        {
           return null;
        }
        return doSomething();
    }   

Дополнительная информация: В моем случае "myParam" должен был быть строкой, int всегда 0.

Ответ 7

request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value?.FirstOrDefault()

современный вариант:)

Ответ 8

Для ASP.NET вы можете получить заголовок непосредственно из параметра в методе контроллера, используя эту простую библиотеку/пакет. Он предоставляет атрибут [FromHeader], как и в ASP.NET Core:). Например:

    ...
    using RazHeaderAttribute.Attributes;

    [Route("api/{controller}")]
    public class RandomController : ApiController 
    {
        ...
        // GET api/random
        [HttpGet]
        public IEnumerable<string> Get([FromHeader("pages")] int page, [FromHeader] string rows)
        {
            // Print in the debug window to be sure our bound stuff are passed :)
            Debug.WriteLine($"Rows {rows}, Page {page}");
            ...
        }
    }