Реализовать поведение привязки параметров по умолчанию в .NET Web API - PullRequest
0 голосов
/ 01 июня 2018

Я реализую настраиваемый механизм связывания параметров, который наследует HttpParameterBinding и будет применять настраиваемое поведение к определенным параметрам.В некоторых случаях я не хочу применять пользовательское поведение, и в этом случае я хочу следовать тому, что Web API делает по умолчанию.Это решение будет принято в ExecuteBindingAsync.Как мне реализовать это поведение по умолчанию в ExecuteBindingAsync?

Я полагаю, что это обычно делается путем простого не применения привязки параметра при регистрации привязки во время запуска (другими словами, обработчик для ParameterBindingRulescollection вернет null, что позволит веб-API связывать привязку по умолчанию с параметром)Однако в моем случае мне нужно решить, применять ли привязку во время выполнения , поэтому мне нужно сделать это в ExecuteBindingAsync.

Я хочу сделать что-то подобное в моем настраиваемом HttpParameterBindingкласс:

public override async Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
{
    if (IsCustomBindingNeeded()) {
        // apply custom binding logic... call SetValue()... I'm good with this part
    }
    else {
        // ***************************************************************
        // This is where I want to use the default implementation.
        // Maybe something like this (using a made up class name):

        await return new WhateverDefaultParameterBinding().ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken);

        // ...or at least be able to call GetValue() and get the correct value and then I can call SetValue()
        // ***************************************************************
    }
}

Я пытался вызвать GetValue (), но он всегда возвращает ноль.Я предполагаю, что есть некоторый дополнительный шаг, который необходимо выполнить, чтобы базовый класс (HttpParameterBinding) мог создать значение.

Я предпочитаю напрямую вызывать любой метод в .NET Framework, содержащий эту логику по умолчанию.Я бы предпочел не дублировать эту логику.

1 Ответ

0 голосов
/ 04 июня 2018

Я нашел решение, хотя я не уверен, что оно идеально.

Я обнаружил Mono-код для DefaultActionValueBinder .Кажется, что метод GetParameterBinding содержит логику, которую я ищу.Однако этот метод защищен, поэтому я не могу вызвать его напрямую.(Я мог бы, вероятно, вызвать публичный метод GetBinding, но я боюсь, что это было бы излишним.) Поэтому мне пришлось бы дублировать логику из GetParameterBinding в моем собственном классе, а также некоторые методы из внутреннего класса TypeHelper, на который он ссылается, чтоВот почему я не думаю, что это решение идеально.Я также не уверен, насколько хорошо проверена реализация Mono, поэтому я обеспокоен тем, что она может содержать ошибки или не поддерживать все сценарии.

Для дальнейшего использования, это текущая реализация Mono DefaultActionValueBinder.GetParameterBinding ()....

    protected virtual HttpParameterBinding GetParameterBinding(HttpParameterDescriptor parameter)
    {
        // Attribute has the highest precedence
        // Presence of a model binder attribute overrides.
        ParameterBindingAttribute attr = parameter.ParameterBinderAttribute;
        if (attr != null)
        {
            return attr.GetBinding(parameter);
        }

        // No attribute, so lookup in global map.
        ParameterBindingRulesCollection pb = parameter.Configuration.ParameterBindingRules;
        if (pb != null)
        {
            HttpParameterBinding binding = pb.LookupBinding(parameter);
            if (binding != null)
            {
                return binding;
            }
        }

        // Not explicitly specified in global map or attribute.
        // Use a default policy to determine it. These are catch-all policies. 
        Type type = parameter.ParameterType;
        if (TypeHelper.IsSimpleUnderlyingType(type) || TypeHelper.HasStringConverter(type))
        {
            // For simple types, the default is to look in URI. Exactly as if the parameter had a [FromUri] attribute.
            return parameter.BindWithAttribute(new FromUriAttribute());
        }

        // Fallback. Must be a complex type. Default is to look in body. Exactly as if this type had a [FromBody] attribute.
        attr = new FromBodyAttribute();
        return attr.GetBinding(parameter);
    }

... и ссылочные методы из TypeHelper.

    internal static bool IsSimpleType(Type type)
    {
        return type.IsPrimitive ||
               type.Equals(typeof(string)) ||
               type.Equals(typeof(DateTime)) ||
               type.Equals(typeof(Decimal)) ||
               type.Equals(typeof(Guid)) ||
               type.Equals(typeof(DateTimeOffset)) ||
               type.Equals(typeof(TimeSpan));
    }

    internal static bool IsSimpleUnderlyingType(Type type)
    {
        Type underlyingType = Nullable.GetUnderlyingType(type);
        if (underlyingType != null)
        {
            type = underlyingType;
        }

        return TypeHelper.IsSimpleType(type);
    }

    internal static bool HasStringConverter(Type type)
    {
        return TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string));
    }
...