0

我正在实现一个自定义参数绑定器,它继承 HttpParameterBinding 并将自定义行为应用于某些参数。在某些情况下,我不想应用自定义行为,在这种情况下,我想遵循默认情况下 Web API 所做的任何事情。这个决定将在 ExecuteBindingAsync 中做出。如何在 ExecuteBindingAsync 中实现此默认行为?

我相信这通常是通过在启动期间注册绑定时简单地不应用参数绑定来完成的(换句话说,ParameterBindingRules 集合的处理程序将返回 null,从而允许 Web 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() 但它总是返回 null。我假设需要执行一些额外的步骤,以便基类 (HttpParameterBinding) 可以创建值。

我的偏好是直接调用 .NET 框架中包含该默认逻辑的任何方法。我宁愿不必重复该逻辑。

4

1 回答 1

1

我找到了一个解决方案,尽管我不确定它是否理想。

我发现了DefaultActionValueBinder 的 Mono 代码。GetParameterBinding 方法似乎包含我正在寻找的逻辑。但是这个方法是受保护的,所以我不能直接调用它。(我可能会调用公共 GetBinding 方法,但我担心这会有点过头了。)所以我必须在我自己的类中复制 GetParameterBinding 的逻辑,以及它引用的内部 TypeHelper 类中的一些方法,其中这就是为什么我不认为这个解决方案是理想的。我也不确定 Mono 实现的审查有多好,所以我担心它可能有问题或不支持所有场景。

供将来参考,这是 DefaultActionValueBinder.GetParameterBinding() 的当前 Mono 实现...

    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));
    }
于 2018-06-04T15:48:09.127 回答