且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

ASP.net MVC - 可空属性自定义属性错误信息

更新时间:2023-02-25 23:04:41

这是有点令人失望看到有关我们要做的工作量时,我们所希望的是通过默认的模型绑定进行的隐含验证自定义错误消息。原因是 DefaultModelBinder 隐藏了一些重要的方法为私有尤其是 GetValueInvalidResource GetValueRequiredResource 。我希望他们会照顾这个未来。

It's little disappointing to see about the amount of work we have to do when all we want is a custom error message for the implicit validations done by the default model binder. The reason is the DefaultModelBinder hides some important methods as private especially the GetValueInvalidResource and GetValueRequiredResource. I hope they will take care of this in future.

我是想给避免为每个类型创建模型活页夹问题的通用的解决方案。

I was trying to give a generic solution for the problem avoiding to create model binders for every type.

老实说,我还没有测试以下实施在所有情况下(例如绑定时的集合),但在基层一样。

因此​​,这里是办法。

So here is the approach.

我们有两个自定义属性,有助于通过自定义错误消息为我们的自定义模型粘合剂。我们可以有一个基类,但是这很好。

We have two custom attributes that helps to pass custom error message for our custom model binder. We could have a base class but that's fine.

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;
    }
}

下面是模型绑定,这是通用的,独立的类型和需要自定义错误消息必需的和无效的验证照顾。

Here is the model binder this is generic and independent of types and takes care of customizing error messages for both required and invalid validations.

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 只是用我们自己的消息时类型无效。

We are overriding the OnPropertyValidated method just to override the implicit required error message thrown by the default model binder, and we are overriding the SetProperty just to use our own message when the type is not valid.

设置我们的定制绑定在Global.asax.cs中的默认

Set our custom binder as the default in Global.asax.cs

ModelBinders.Binders.DefaultBinder = new ExtendedModelBinder();

和可以装饰你的属性这样的..

And you can decorate your properties like this..

[PropertyValueRequired("this field is required")]
[PropertyValueInvalid("type is invalid")]
[Display(Name = "Code Postal")]
public int? CodePostal { get; set; }