当我们想要的只是默认模型绑定器完成的隐式验证的自定义错误消息时,看到我们必须做的工作量有点令人失望。原因是DefaultModelBinder 隐藏了一些重要的私有方法,尤其是GetValueInvalidResource 和GetValueRequiredResource。我希望他们以后会处理好这件事。
我试图为避免为每种类型创建模型绑定器的问题提供通用解决方案。
老实说,我没有在所有情况下(例如绑定集合时)测试以下实现,但在基本级别中测试过。
所以这是方法。
我们有两个自定义属性,可以帮助为我们的自定义模型绑定器传递自定义错误消息。我们可以有一个基类,但这很好。
public class PropertyValueInvalidAttribute: Attribute
{
public string ErrorMessage { get; set; }
public PropertyValueInvalid(string errorMessage)
{
ErrorMessage = errorMessage;
}
}
public class PropertyValueRequiredAttribute: Attribute
{
public string ErrorMessage { get; set; }
public PropertyValueRequired(string errorMessage)
{
ErrorMessage = errorMessage;
}
}
这是模型绑定器,它是通用的,独立于类型,负责为必需和无效验证自定义错误消息。
public class ExtendedModelBinder : DefaultModelBinder
{
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
if (propertyDescriptor.Attributes.OfType<PropertyValueInvalidAttribute>().Any())
{
var attr = propertyDescriptor.Attributes.OfType<PropertyValueInvalidAttribute>().First();
foreach (ModelError error in bindingContext.ModelState[propertyDescriptor.Name]
.Errors.Where(err => String.IsNullOrEmpty(err.ErrorMessage) && err.Exception != null)
.ToList())
{
for (Exception exception = error.Exception; exception != null; exception = exception.InnerException)
{
if (exception is FormatException)
{
bindingContext.ModelState[propertyDescriptor.Name].Errors.Remove(error);
bindingContext.ModelState[propertyDescriptor.Name].Errors.Add(attr.ErrorMessage);
break;
}
}
}
}
}
protected override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
{
if (propertyDescriptor.Attributes.OfType<PropertyValueRequiredAttribute>().Any())
{
var attr = propertyDescriptor.Attributes.OfType<PropertyValueRequiredAttribute>().First();
var isTypeAllowsNullValue = (!propertyDescriptor.PropertyType.IsValueType || Nullable.GetUnderlyingType(propertyDescriptor.PropertyType) != null);
if (value == null && !isTypeAllowsNullValue)
{
bindingContext.ModelState[propertyDescriptor.Name].Errors.Clear();
bindingContext.ModelState.AddModelError(propertyDescriptor.Name, attr.ErrorMessage);
}
}
base.OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, value);
}
}
我们重写 OnPropertyValidated 方法只是为了重写默认模型绑定器抛出的隐式必需错误消息,并且我们重写 SetProperty 只是为了在类型无效时使用我们自己的消息。
将我们的自定义活页夹设置为 Global.asax.cs 中的默认值
ModelBinders.Binders.DefaultBinder = new ExtendedModelBinder();
你可以像这样装饰你的财产..
[PropertyValueRequired("this field is required")]
[PropertyValueInvalid("type is invalid")]
[Display(Name = "Code Postal")]
public int? CodePostal { get; set; }