Получите значение List‹T› внутри пользовательского IValueProvider ASP.NET MVC 5.2.3

После борьбы с этой проблемой в течение почти 2 дней я решил прийти сюда и задать этот вопрос - я сделаю все возможное, чтобы быть максимально ясным.

Прежде всего, то, что я пытаюсь сделать, довольно просто: реализовать собственный IValueProvider, который расшифровывает параметры действия внутри контроллера ASP.NET MVC — эта часть выполнена и работает отлично; моя проблема начинается в тот момент, когда у меня есть список среди параметров действия, потому что я не могу точно указать значения этого списка из контекста запроса.

Давайте сделаем это шаг за шагом.

Во-первых, у меня есть атрибут, который украшает нужные действия:

public class EncryptedQueryStringValuesAttribute : FilterAttribute, IAuthorizationFilter
{
    /// <summary>
    /// Called when authorization is required.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        filterContext.Controller.ValueProvider = new EncryptedQueryStringValuesProvider(
            filterContext.RequestContext, filterContext.ActionDescriptor);
    }
}

Затем у меня есть конструктор поставщика значений, в котором я инициализирую то, что мне нужно:

public EncryptedQueryStringValuesProvider(RequestContext requestContext, ActionDescriptor actionDescriptor)
{
    this.requestContext = requestContext;
    this.actionDescriptor = actionDescriptor;

    this.prefixContainer =
        new Lazy<PrefixContainer>(
            () => new PrefixContainer(this.requestContext.HttpContext.Request.Params.AllKeys), true);
}

В качестве примечания, prefixContainer действительно работает как шарм - если я проверяю, есть ли префикс в контейнере, он работает отлично независимо от типа префикса (это означает, что не имеет значения, является ли параметр коллекцией или простой параметр).

Проверка наличия параметра внутри провайдера значений выполняется так:

public bool ContainsPrefix(string prefix)
{
    return this.prefixContainer.Value.ContainsPrefix(prefix);
}

Как я уже сказал, это дает мне правильные значения.

Теперь самое неприятное — метод GetValue:

public ValueProviderResult GetValue(string key)
{
    string[] rawValue = this.requestContext.HttpContext.Request.Params.GetValues(key);
    string attemptedValue = this.requestContext.HttpContext.Request.Params[key];
    return new ValueProviderResult(rawValue, attemptedValue, CultureInfo.CurrentCulture);
}

rawValue, извлеченный прямо сейчас, имеет значение null и логично быть нулевым, потому что в моем запросе нет ключа с таким именем - все, что у меня есть, это просто коллекция, подобная этой:

[25]: "SupplierInvoices[x].PaymentToInvoiceExchangeRate"
[26]: "SupplierInvoices[x].AmountToPayOnGivenInvoice"
[27]: "SupplierInvoices[x].OpenInvoiceId"
[28]: "SupplierInvoices[x].OpenInvoiceTimestamp"

С другой стороны, я прекрасно понимаю, что я должен использовать конструкцию this.prefixContainer.Value.GetKeysFromPrefix(prefix);, чтобы получить все ключи, которые есть в моем запросе, и на его основе как-то соединить эти ключи и отдать их обратно в ValueProviderResult, НО НЕ ИМЕЮ ИДЕИ КАК! :-)

Пожалуйста, кто-нибудь может объяснить, как эти значения могут быть объединены, чтобы быть переданными обратно в ValueProviderResult для правильной интерпретации?

СПАСИБО!


person Edi    schedule 06.04.2016    source источник


Ответы (1)


После борьбы с этой проблемой в течение последних недель я решил, что должен изменить подход и попытаться не изобретать велосипед. Прежде всего, я должен бросить все, что у меня есть, связанное с обработкой коллекции и все эти вещи - поэтому все, что касается this.prefixContainer, должно уйти - если я не могу найти простой ключ в HTTPContext, я не должен его обрабатывать и оставить его для остальные провайдеры.

Здесь возникает еще одна проблема - как я могу «сказать» моему поставщику значений «отпустить» о ключе ??? Изначально я думал, что если я верну false в методе ContainsPrefix, то обработка переключится на следующий IValueProvider в очереди - это не так - и метод ContainsPrefix должен возвращать null и более того, метод GetValue должен возвращать null и только при этих обстоятельствах обработка перейдет к следующему IValueProvider.

ОК - теперь, когда у меня есть все эти вещи, как я могу использовать свой поставщик значений без ValueProviderFactory, зарегистрированного глобально, потому что, как я уже сказал, обработка происходит только для действия, "подписанного" с заданным атрибутом. Ответ такой: вместо того, чтобы создавать пользовательский ValueProvider в Controller, используя такой код атрибута:

filterContext.Controller.ValueProvider = new EncryptedQueryStringValuesProvider(filterContext.RequestContext, filterContext.ActionDescriptor);

Я должен добавить своего поставщика значений в начало списка:

ValueProviderCollection valueProviderCollection = filterContext.Controller.ValueProvider as ValueProviderCollection;
if (valueProviderCollection == null)
throw new NullReferenceException("filterContext.Controller.ValueProvider as ValueProviderCollection");

valueProviderCollection.Insert(0, new EncryptedQueryStringValuesProvider(filterContext.RequestContext, filterContext.ActionDescriptor));

Я надеюсь, что это поможет кому-то. :-)

person Edi    schedule 19.04.2016