【问题标题】:Create a custom Model Binder for a custom type为自定义类型创建自定义模型绑定器
【发布时间】:2017-02-28 04:09:28
【问题描述】:

在一个 ASP.NET CORE 1.1 项目中,我有以下模型:

public class GetProductsModel {
  public OrderExpression OrderBy { get; set; }
}

OrderExpression 是一个具有以下方法的类:

Boolean TryParse(String value, out OrderExpression expression)

该方法从String 创建一个OrderExpression 实例并且可以使用:

OrderExpression expression;

Boolean parsed = OrderExpression.TryParse(value, out expression);

如何为OrderExpression 类型的属性创建自定义模型绑定器?

【问题讨论】:

  • 您是在尝试绑定 OrderExpression 的属性,还是尝试将 OrderExpression 绑定到控制器/动作的输入,这将是一个字符串输入,然后映射到一个 OrderExpression 对象?
  • @AshleyLee 我正在尝试将 OrderExpression 绑定到控制器操作的输入...这有帮助吗?
  • @MiguelMoura,只是想知道您是否有机会尝试我提出的解决方案?
  • @DanielJ.G.是的,就是这么做的……我把它标记为答案。

标签: asp.net-core


【解决方案1】:

我假设在您的请求数据中有一个属性orderBy,您希望使用OrderExpression.TryParse 将其绑定到OrderExpression

假设您的 OrderExpression 类如下所示,我在其中提供了您的 TryParse 方法的非常简单的实现:

public class OrderExpression
{
    public string RawValue { get; set; }
    public static bool TryParse(string value, out OrderExpression expr)
    {
        expr = new OrderExpression { RawValue = value };
        return true;
    }
}

然后你可以创建一个模型绑定器,它基本上获取原始字符串值并调用OrderExpression.TryParse

public class OrderExpressionBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {            
        var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (values.Length == 0) return Task.CompletedTask;

        // Attempt to parse
        var stringValue = values.FirstValue;
        OrderExpression expression;
        if (OrderExpression.TryParse(stringValue, out expression))
        {
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, expression, stringValue);
            bindingContext.Result = ModelBindingResult.Success(expression);
        }

        return Task.CompletedTask;
    }
}

您还需要一个新的模型绑定器提供程序,它只为 OrderExpression 类型返回您的新绑定器:

public class OrderExpressionBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        return context.Metadata.ModelType == typeof(OrderExpression) ? new OrderExpressionBinder() : null;
    }
}

// It should be registered in your Startup class, adding it to the ModelBinderProviders collection:
services.AddMvc(opts => {
    opts.ModelBinderProviders.Insert(0, new OrderExpressionBinderProvider());
});

有了这个,您将能够绑定控制器操作的OrderExpression 参数。类似于以下示例:

[HttpPost]
public IActionResult Products([FromBody]OrderExpression orderBy) 
{
    return Ok();
}

$.ajax({
    method: 'POST', 
    dataType: 'json', 
    url: '/home/products', 
    data: {orderby: 'my orderby expression'}
});

但是,您还需要做一些其他事情才能发送 json 并将其绑定到像 GetProductsModel 这样内部包含 OrderExpression 的复杂模型。我说的是这样的场景:

[HttpPost]
public IActionResult Products([FromBody]GetProductsModel model)
{
    return Ok();
}

public class GetProductsModel
{
    public OrderExpression OrderBy { get; set; }
}

$.ajax({
    method: 'POST', 
    dataType: 'json', 
    contentType: 'application/json; charset=utf-8', 
    url: '/home/products', 
    data: JSON.stringify({orderby: 'my orderby expression'})
});

在这种情况下,ASP.Net Core 将仅使用 Newtonsoft.Json 作为 InputFormatter 并将接收到的 json 转换为 GetProductsModel 模型的实例,而不尝试将新的 OrderExpressionBinderProvider 用于内部属性。

幸运的是,您还可以通过创建 JsonConverter 告诉 Newtonsoft.Json 如何格式化 OrderExpression 类型的属性:

public class OrderExpressionJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(OrderExpression);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var stringValue = reader.Value?.ToString();
        OrderExpression expression;
        if (OrderExpression.TryParse(stringValue, out expression))
        {
            return expression;
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

应该在你的 Startup 类中注册:

services.AddMvc(opts => {
    opts.ModelBinderProviders.Insert(0, new OrderExpressionBinderProvider());

}).AddJsonOptions(opts => {
    opts.SerializerSettings.Converters.Add(new OrderExpressionJsonConverter());
});

现在你终于可以处理这两种情况了:)

【讨论】:

    猜你喜欢
    • 2011-02-08
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 2012-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多