c# - 在 Web API 2 的自定义绑定(bind)器中使用默认 IModelBinder

标签 c# .net asp.net-web-api2 model-binding

如何在自定义 IModelBinder 中调用 Web API 中的默认模型绑定(bind)器?我知道 MVC 有一个默认的 Binder ,但我不能将它与 Web API 一起使用。我只想使用默认的 Web API 绑定(bind)器,然后运行一些自定义逻辑(以避免重新发明轮子)。

public class CustomBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        // Get default binding (can't mix Web API and MVC)
        var defaultMvcBinder = System.Web.ModelBinding.ModelBinders.Binders.DefaultBinder;
        var result = defaultMvcBinder.BindModel(actionContext, bindingContext); // Won't work
        if (result == false) return false;
        // ... set additional model properties
        return true;
    }
}

最佳答案

为了防止其他人偶然发现这个问题,我必须使用激活上下文来实现自定义模型绑定(bind)器,因为 Web API 中没有任何内容可以重用。这是我针对需要支持的有限场景使用的解决方案。

使用

下面的实现允许我让任何模型选择使用 JsonProperty 进行模型绑定(bind),但如果未提供,则默认仅使用属性名称。它支持标准 .NET 类型(字符串、整数、 double 等)的映射。尚未完全准备好投入生产,但到目前为止它满足了我的用例。

[ModelBinder(typeof(AttributeModelBinder))]
public class PersonModel
{
    [JsonProperty("pid")]
    public int PersonId { get; set; }

    public string Name { get; set; }
}

这允许在请求中映射以下查询字符串:

/api/endpoint?pid=1&name=test

实现

首先,该解决方案定义一个映射属性来跟踪模型的源属性以及在从值提供程序设置值时要使用的目标名称。

public class MappedProperty
{
    public MappedProperty(PropertyInfo source)
    {
        this.Info = source;
        this.Source = source.Name;
        this.Target = source.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName ?? source.Name;
    }
    public PropertyInfo Info { get; }
    public string Source { get; }
    public string Target { get; }
}

然后,定义一个自定义模型绑定(bind)器来处理映射。它缓存反射的模型属性,以避免在后续调用中重复反射。它可能还没有完全准备好投入生产,但初步测试很有希望。

public class AttributeModelBinder : IModelBinder
{
    public static object _lock = new object();
    private static Dictionary<Type, IEnumerable<MappedProperty>> _mappings = new Dictionary<Type, IEnumerable<MappedProperty>>();


    public IEnumerable<MappedProperty> GetMapping(Type type)
    {
        if (_mappings.TryGetValue(type, out var result)) return result; // Found
        lock (_lock)
        {
            if (_mappings.TryGetValue(type, out result)) return result; // Check again after lock
            return (_mappings[type] = type.GetProperties().Select(p => new MappedProperty(p)));
        }
    }

    public object Convert(Type target, string value)
    {
        try
        {
            var converter = TypeDescriptor.GetConverter(target);
            if (converter != null)
                return converter.ConvertFromString(value);
            else
                return target.IsValueType ? Activator.CreateInstance(target) : null;
        }
        catch (NotSupportedException)
        {
            return target.IsValueType ? Activator.CreateInstance(target) : null;
        }
    }

    public void SetValue(object model, MappedProperty p, IValueProvider valueProvider)
    {
        var value = valueProvider.GetValue(p.Target)?.AttemptedValue;
        if (value == null) return;
        p.Info.SetValue(model, this.Convert(p.Info.PropertyType, value));
    }

    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        try
        {
            var model = Activator.CreateInstance(bindingContext.ModelType);
            var mappings = this.GetMapping(bindingContext.ModelType);
            foreach (var p in mappings)
                this.SetValue(model, p, bindingContext.ValueProvider);
            bindingContext.Model = model;
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
    }
}

关于c# - 在 Web API 2 的自定义绑定(bind)器中使用默认 IModelBinder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51090215/

相关文章:

c# - 如何使用 Core 2.2 中的身份在浏览器关闭 15 分钟后保持 session 事件?

.net - 文化和 UICulture 有什么区别?

c# - ASP.Net Web API 和 JQuery Ajax - 如何处理 401 未经授权的结果

c# - 无法在 Web API 响应中序列化 ByteArraycontent 类型

.net - 垃圾收集不会减少当前的内存使用量 - 在 Release模式下。为什么?

asp.net-core - 具有队列处理功能的 .NET 核心 Web api

c# - 从 html 文档中提取 html 片段

c# - 事件支持 WP 8.1

c# - 领域服务可以引发领域事件吗?

.net - SSL 失败 - 将 HttpWebRequest 与客户端证书一起使用时收到 SSL/TLS 异常