2

我的视图模型中有一个可以接受整数和可为空值的属性:

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

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

The field Code Postal must be a number.

谢谢

4

3 回答 3

1

替代方案 - 重写资源字符串

最简单的方法是替换默认验证资源字符串。This 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;
}

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

于 2012-06-21T08:37:44.943 回答
1

当我们想要的只是默认模型绑定器完成的隐式验证的自定义错误消息时,看到我们必须做的工作量并不令人失望。原因是DefaultModelBinder隐藏了一些重要的方法作为私有方法,尤其是GetValueInvalidResourceand 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; }
于 2012-06-21T11:04:44.307 回答
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>
}
于 2012-06-21T08:49:32.640 回答