Статьи

Web API — пользовательский поставщик значений заголовков


Есть случаи, когда вы должны отправить сложный объект на серверную часть, используя запросы GET.
Чтобы добиться этого, вы должны использовать привязку URI, украсив параметр метода контроллера атрибутом [FromURI]. Поэтому, если у вас есть объект с именем «Criteria», который состоит из свойств «CriteriaA» и «CriteriaB», ваш запрос GET будет выглядеть следующим образом:
http: // myapplication / api / getfoobycriteria? CriteriaA = valueA & CriteriaB = valueB. 


А что, если объект, который вы хотите отправить, является слишком сложным или большим в своей сериализованной форме? Привязка URI все еще может использоваться только для того, чтобы у вас получился довольно уродливый / длинный URL, который для некоторых может не быть проблемой. Что если запрос GET не должен быть кэширован браузером? В этом случае должны использоваться соответствующие заголовки, чтобы сообщить браузеру, что ответы на запрос GET не должны кэшироваться. Существует специальный способ отправки сложных объектов на серверную часть с помощью запросов GET. Та же функциональность может быть достигнута с помощью пользовательского заголовка, который будет содержать сериализованный сложный объект. Затем этот пользовательский заголовок будет отправлен на серверную часть, где по конвейеру Web-API будет десериализован и привязан к параметру метода вызываемого контроллера.Для достижения ранее упомянутой функциональности должен быть реализован пользовательский поставщик значений заголовка. 


Существует один интерфейс и один класс, оба являются частью пространства имен System.Web.Htpp.ValueProviders, которое будет использоваться, IValueProvider и ValueProviderFactory.
Роль провайдеров значений заключается в том, чтобы получать необработанные данные из входящего запроса и передавать их в связыватели моделей, где связыватели моделей преобразуют необработанные значения в соответствующие типы данных (сложные объекты, коллекции сложных объектов и т. Д.) С использованием различных механизмов, таких как -сериализация, приведение типов и так далее. Вот код для нашего пользовательского универсального HeaderValueProvider:

using System;
using System.Globalization;
using System.Net.Http.Headers;
using System.Linq;
using System.Web.Http.ValueProviders;
using Newtonsoft.Json;
 
namespace MyProject.Infrastructure.WebAPI
{
    public class HeaderValueProvider<T> : IValueProvider where T : class
    {
        private const string HeaderPrefix = "X-";
        private readonly HttpRequestHeaders _headers;

        public HeaderValueProvider(HttpRequestHeaders headers)
        {
            _headers = headers;
        }

        public bool ContainsPrefix(string prefix)
        {
            var contains = _headers.Any(h => h.Key.Contains(HeaderPrefix + prefix));
            return contains;
        }
 
        public ValueProviderResult GetValue(string key)
        {
                var contains = _headers.Any(h => h.Key.Contains(HeaderPrefix + key));
                if (!contains)
                    return null;
 
                var value = _headers.GetValues(HeaderPrefix + key).First();
                var obj = JsonConvert.DeserializeObject<T>(value, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
                return new ValueProviderResult(obj, value, CultureInfo.InvariantCulture);
        }
    }

Прежде всего, у класса HeaderValueProvider есть конструктор, который принимает один параметр, который имеет тип HttpRequestHeaders, который является коллекцией заголовков запроса. Это место, где будет искать пользовательский заголовок. Класс HeaderValueProvider действительно прост, кроме конструктора, он имеет только два метода, ContainsPrefix и GetValue. Важно отметить, что имя пользовательского заголовка должно иметь префикс, в этом случае «X-» представляет используемый префикс. Метод ContainsPrefix не делает ничего другого, кроме проверки того, является ли какой-либо из заголовков запроса настраиваемым на основе определенного префикса. Он возвращает true, если коллекция заголовков запроса содержит пользовательский заголовок, или false, если нет. Последний метод HeaderValueProvider — это GetValue только с одним параметром типа string (string key).Это ключ, который состоит из префикса и будет определять желаемый пользовательский заголовок, значение которого должно быть получено. По сути, ключ — это имя пользовательского заголовка без префикса, и очень важно понимать, что для того, чтобы значение идентифицированного пользовательского заголовка было привязано к параметру метода контроллера, имя параметра и имя пользовательского заголовка должны быть идентичны (исключая приставка). Поэтому, если у вас есть метод контроллера public HttpResponseMessage GetFooByCriteria (критерии критериев), полное имя настраиваемого заголовка должно быть «X-критериями». Как только значение извлекается из пользовательского заголовка, оно десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNПо сути, ключ — это имя пользовательского заголовка без префикса, и очень важно понимать, что для того, чтобы значение идентифицированного пользовательского заголовка было привязано к параметру метода контроллера, имя параметра и имя пользовательского заголовка должны быть идентичны (исключая приставка). Поэтому, если у вас есть метод контроллера public HttpResponseMessage GetFooByCriteria (критерии критериев), полное имя настраиваемого заголовка должно быть «X-критериями». Как только значение извлекается из пользовательского заголовка, оно десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNПо сути, ключ — это имя настраиваемого заголовка без префикса, и очень важно понимать, что для того, чтобы значение идентифицированного настраиваемого заголовка было привязано к параметру метода контроллера, имя параметра и имя настраиваемого заголовка должны быть идентичны (исключая приставка). Поэтому, если у вас есть метод контроллера public HttpResponseMessage GetFooByCriteria (критерии критериев), полное имя настраиваемого заголовка должно быть «X-критериями». Как только значение извлекается из пользовательского заголовка, оно десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNимя параметра и имя пользовательского заголовка должны быть идентичны (исключая префикс). Поэтому, если у вас есть метод контроллера public HttpResponseMessage GetFooByCriteria (критерии критериев), полное имя настраиваемого заголовка должно быть «X-критериями». Как только значение извлекается из пользовательского заголовка, оно десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNимя параметра и имя пользовательского заголовка должны быть идентичны (исключая префикс). Поэтому, если у вас есть метод контроллера public HttpResponseMessage GetFooByCriteria (критерии критериев), полное имя настраиваемого заголовка должно быть «X-критериями». Как только значение извлекается из пользовательского заголовка, оно десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNон десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDNон десериализуется в нужный тип объекта (в данном конкретном случае используется JSON) и возвращается новый ValueProviderResult. По данным MSDN 

 


ValueProviderResult представляет результат привязки значения (например, из сообщения формы или строки запроса) к свойству аргумента метода действия или к самому аргументу. 

Последнее, что нужно сделать, — это зарегистрировать HeadeValueProvider в конвейере веб-API, создав собственный класс ValueProviderFactory.
ValueProviderFactory представляет фабрику для создания объектов провайдера значений.
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
 
namespace MyProject.Infrastructure.WebAPI
{
    public class HeaderValueProviderFactory<T> : ValueProviderFactory where T : class
    {
        public override IValueProvider GetValueProvider(HttpActionContext actionContext)
        {
            var headers = actionContext.ControllerContext.Request.Headers;
            return new HeaderValueProvider<T>(headers);
        }
    }
}

Наконец, вот краткий пример использования HeaderValueProvider:

public HttpResponseMessage GetFooByCriteria([ValueProvider(typeof(HeaderValueProviderFactory<Criteria>))] Criteria criteria)
{
.......
}