ASP.net MVC - Custom attribute error message with nullable properties

前端 未结 3 1638
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-23 05:42

I am having a property in my View Model which can accept integer and nullable values:

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


        
3条回答
  •  一个人的身影
    2021-01-23 06:31

    You could write a metadata aware attribute:

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

    and a custom model binder that uses this attribute because it is the default model binder that adds the hardcoded error message you are seeing when it binds those integral types from the request:

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

    which will be registered in Application_Start:

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

    From this moment on things get pretty standard.

    View model:

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

    Controller:

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

    View:

    @model MyViewModel
    
    @using (Html.BeginForm())
    {
        @Html.EditorFor(x => x.CodePostal)
        @Html.ValidationMessageFor(x => x.CodePostal)
        
    }
    

提交回复
热议问题