ModelState.IsValid even when it should not be?

前端 未结 10 1328
说谎
说谎 2020-11-29 03:14

I have API where I need to validate my user model. I choose an approach where I create different classes for Create/Edit actions to avoid mass-assignment and divide validati

10条回答
  •  [愿得一人]
    2020-11-29 03:49

    I was looking for a solution to this problem and came out here first. After some further research I have realized the following solution:

    How do you use my solution? You can register it globally:

    config.Filters.Add(new ValidateModelStateAttribute());
    

    Or use it on demand for a class

    [ValidateModelState]
    public class UsersController : ApiController
    {...
    

    or for a methode

    [ValidateModelState]
    public IHttpActionResult Create([Required] UserModel data)
    {...
    

    As you can see, a [System.ComponentModel.DataAnnotations.Required] atribute has been placed in the method parameter. This indicates that the model is required and can not be null.

    You can also use with a custom message:

    [ValidateModelState]
    public IHttpActionResult Create([Required(ErrorMessage = "Custom message")] UserModel data)
    {...
    

    Here is my code:

    using System;
    using System.Collections.Concurrent;
    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Net.Http;
    using System.Reflection;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    
    namespace your_base_namespace.Web.Http.Filters
    {
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
        public class ValidateModelStateAttribute : ActionFilterAttribute
        {
            private delegate void ValidateHandler(HttpActionContext actionContext);
    
            private static readonly ConcurrentDictionary _validateActionByActionBinding;
    
            static ValidateModelStateAttribute()
            {
                _validateActionByActionBinding = new ConcurrentDictionary();
            }
    
            public override void OnActionExecuting(HttpActionContext actionContext)
            {
                GetValidateHandler(actionContext.ActionDescriptor.ActionBinding)(actionContext);
    
                if (actionContext.ModelState.IsValid)
                    return;
    
                actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
            }
    
            private ValidateHandler GetValidateHandler(HttpActionBinding actionBinding)
            {
                ValidateHandler validateAction;
    
                if (!_validateActionByActionBinding.TryGetValue(actionBinding, out validateAction))
                    _validateActionByActionBinding.TryAdd(actionBinding, validateAction = CreateValidateHandler(actionBinding));
    
                return validateAction;
            }
    
            private ValidateHandler CreateValidateHandler(HttpActionBinding actionBinding)
            {
                ValidateHandler handler = new ValidateHandler(c => { });
    
                var parameters = actionBinding.ParameterBindings;
    
                for (int i = 0; i < parameters.Length; i++)
                {
                    var parameterDescriptor = (ReflectedHttpParameterDescriptor)parameters[i].Descriptor;
                    var attribute = parameterDescriptor.ParameterInfo.GetCustomAttribute(true);
    
                    if (attribute != null)
                        handler += CreateValidateHandler(attribute, parameterDescriptor.ParameterName);
                }
    
                return handler;
            }
    
            private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, string name)
            {            
                return CreateValidateHandler(attribute, new ValidationContext(new object()) { MemberName = name });
            }
    
            private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, ValidationContext context)
            {
                return new ValidateHandler(actionContext =>
                {
                    object value;
                    actionContext.ActionArguments.TryGetValue(context.MemberName, out value);
    
                    var validationResult = attribute.GetValidationResult(value, context);
                    if (validationResult != null)
                        actionContext.ModelState.AddModelError(context.MemberName, validationResult.ErrorMessage);
                });
            }
        }
    }
    

提交回复
热议问题