问题描述
在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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!