ASP.net MVC - 具有可空属性的自定义属性错误消息

时间:2012-06-21 08:34:16

标签: asp.net-mvc asp.net-mvc-3 viewmodel

我在View Model中有一个可以接受整数和可空值的属性:

    [Display(Name = "Code Postal")]
    public int? CodePostal { get; set; }

当我输入字符串值时,如何显示除默认值之外的其他消息:

The field Code Postal must be a number.

由于

3 个答案:

答案 0 :(得分:1)

替代 - 重写资源字符串

最简单的方法是替换默认验证资源字符串。 这other SO answer将帮助您。

但你必须记住,这些字符串将用于所有模型,而不仅仅是某些类的特定属性。


  

注意:根据Darin(我没有测试代码),我正在回答我的一部分答案。通过更改资源字符串的简化方法仍然有效。我自己做了,我知道它有效。

<击>

<击>

正则表达式属性

为您的媒体资源添加其他属性:

[Display(Name = "Code Postal")]
[RegularExpression("\d+", ErrorMessage = "I'm now all yours...")]
public int? CodePostal { get; set; }

即使你在非字符串属性上设置正则表达式,这仍然可以工作。如果我们查看验证码,就像这样:

public override bool IsValid(object value)
{
    this.SetupRegex();
    string text = Convert.ToString(value, CultureInfo.CurrentCulture);
    if (string.IsNullOrEmpty(text))
    {
        return true;
    }

    Match match = this.Regex.Match(text);
    return match.Success && match.Index == 0 && match.Length == text.Length;
}

正如我们所看到的,这个验证器会自动将值转换为字符串。因此,如果您的值是一个数字,那么它并不重要,因为它将转换为字符串并由正则表达式验证。

答案 1 :(得分:1)

您可以编写元数据感知属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class MustBeAValidIntegerAttribute : Attribute, IMetadataAware
{
    public MustBeAValidIntegerAttribute(string errorMessage)
    {
        ErrorMessage = errorMessage;
    }

    public string ErrorMessage { get; private set; }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["mustbeavalidinteger"] = ErrorMessage;
    }
}

和使用此属性的自定义模型绑定器,因为它是默认的模型绑定器,它添加了从请求中绑定这些整数类型时看到的硬编码错误消息:

public class NullableIntegerModelBinder: DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (!bindingContext.ModelMetadata.AdditionalValues.ContainsKey("mustbeavalidinteger"))
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        var mustBeAValidIntegerMessage = bindingContext.ModelMetadata.AdditionalValues["mustbeavalidinteger"] as string;
        if (string.IsNullOrEmpty(mustBeAValidIntegerMessage))
        {
            return base.BindModel(controllerContext, bindingContext);
        }

        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (value == null)
        {
            return null;
        }

        try
        {
            return value.ConvertTo(typeof(int?));
        }
        catch (Exception)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, mustBeAValidIntegerMessage);
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
        }

        return null;
    }
}

将在Application_Start注册:

ModelBinders.Binders.Add(typeof(int?), new NullableIntegerModelBinder());

从这一刻起,事情变得相当标准。

查看型号:

public class MyViewModel
{
    [Display(Name = "Code Postal")]
    [MustBeAValidInteger("CodePostal must be a valid integer")]
    public int? CodePostal { get; set; }
}

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

查看:

@model MyViewModel

@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.CodePostal)
    @Html.ValidationMessageFor(x => x.CodePostal)
    <button type="submit">OK</button>
}

答案 2 :(得分:1)

当我们想要的是默认模型绑定器完成的隐式验证的自定义错误消息时,看到我们必须完成的工作量有点令人失望。原因是DefaultModelBinder将某些重要方法隐藏为私有,尤其是GetValueInvalidResourceGetValueRequiredResource。我希望他们将来能够解决这个问题。

我试图为问题提供通用解决方案,避免为每种类型创建模型绑定器。

老实说,我没有在所有情况下测试以下实现(例如,绑定集合时),但是在基本级别

所以这就是方法。

我们有两个自定义属性,有助于为自定义模型绑定器传递自定义错误消息。我们可以有一个基类,但没关系。

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