本文介绍了为自定义类型创建自定义Model Binder的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In an ASP.NET CORE 1.1 project I have the following model:

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

OrderExpression是具有以下方法的类:

OrderExpression is a class which has the following method:

Boolean TryParse(String value, out OrderExpression expression)

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

The method creates a OrderExpression instance from a String and can be used:

OrderExpression expression;

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

如何为类型为 OrderExpression 的属性创建自定义Model Binder?

How can I create a custom Model Binder to properties of type OrderExpression?

推荐答案

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

I assume that within your request data there is a property orderBy that you want to bind into an OrderExpression using OrderExpression.TryParse.

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

Let's assume your OrderExpression class looks like follows, where I have provided a very simple implementation of your TryParse method:

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 :

Then you could create a model binder which basically gets the raw string value and calls 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 类型返回新的资料夹:

You will also need a new model binder provider, which returns your new binder just for the OrderExpression type:

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 参数.如以下示例所示:

With this in place, you will be able to bind OrderExpression parameters of the controller actions. Something like in the following example:

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

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

不过,您还需要做一些其他事情,才能发送json并将其绑定到内部包含 OrderExpression 的复杂模型,例如 GetProductsModel .我说的是这样的情况:

However there is something else that needs to be done for you to be able to send a json and bind it to a complex model like GetProductsModel which internally contains an OrderExpression. I am talking about a scenario like this:

[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 作为内部属性.

In that scenario ASP.Net Core will just use Newtonsoft.Json as the InputFormatter and convert the received json into an instance of the GetProductsModel model, without trying to use the new OrderExpressionBinderProvider for the internal property.

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

Luckily, you can also tell Newtonsoft.Json how to format properties of OrderExpression type by creating your JsonConverter:

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类中注册:

Which should be registered in your Startup class:

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

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

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

Now you will finally be able to handle both scenarios :)

这篇关于为自定义类型创建自定义Model Binder的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-28 04:05