C#/.Net · 2019年4月28日 0

WebApi或Asp.Net MVC中参数验证无效

问题

看下下边的代码,[Range(10, 20)]这个验证在这里永远不会生效,本文的目的就是让他生效。

public class ValuesController : ApiController
{
    [HttpGet]
    [ValidateModel]
    public string novalid([Range(10, 20)] int id)
    {
        return "value";
    }
}

接下来详细说明一下这种情况:
在.net下Webapi项目中如果使用到了任何ValidationAttribute(包括StringLengthAttribute,RangeAttribute,Required等等)作为URL参数验证,则不会有效果,无论是原有的还是自己继承后的都不生效;这个验证只有在封装好的Model中才会生效,先看如下代码:

ValuesController
using System.ComponentModel.DataAnnotations;
using System.Web.Http;

namespace validation_test.Controllers
{
    public class Model
    {
        [Range(10, 20)]
        public int id { get; set; }
    }
    public class Model_1
    {
        public int id { get; set; }
    }

    [RoutePrefix("values")]
    public class ValuesController : ApiController
    {
        /// <summary>
        /// 方法一
        /// 结果:验证不起作用
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [Route("novalid/{id}")]
        [HttpGet]
        [ValidateModel]
        public string novalid([Range(10, 20)] int id)
        {
            return "value";
        }

        /// <summary>
        /// 方法二
        /// 结果:验证起作用
        /// </summary>
        /// <param name="test"></param>
        /// <returns></returns>
        [Route("valid_model")]
        [HttpPost]
        [ValidateModel]
        public string valid_model(Model test)
        {
            return "value";
        }

        /// <summary>
        /// 方法三
        /// 结果:验证不起作用
        /// </summary>
        /// <param name="test"></param>
        /// <returns></returns>
        [Route("valid_model_1")]
        [HttpPost]
        [ValidateModel]
        public string valid_model_1([Required]Model_1 test)
        {
            return "value";
        }

    }

}
ValidateModel
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace validation_test
{
    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }
}

方法一返回结果:


方法二返回结果:


方法三返回结果:


在这里虽然一个方法是GET,另一个是POST,但是经过测试请求类型不改变测试结果。

从结果上看,只有方法二是触发了ValidationAttribute,但是ValidationAttribute特性是可以用于方法参数的(如下图);

也就是说:在.Net WebApi底层的架构里并没有触发Parameter的验证特性,只触发了Property的验证特性(可能也包含Field,未验证)
(MVC与WebApi是同样的情况)

不知.Net此种做法的目的是什么,还是接着看如何修复吧。

解决方案

在原来的ValuesController里新加如下:

[Route("valid/{id}")]
[HttpGet]
[ValidateParameter]
public string valid([Range(10, 20)] int id)
{
    return "value";
}
ValidateParameter
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;

namespace validation_test
{
    public class ValidateParameterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext context)
        {
            var descriptor = context.ActionDescriptor as HttpActionDescriptor;
            if (descriptor != null)
            {
                //取得所有参数描述集合
                var parameters = descriptor.GetParameters();

                foreach (HttpParameterDescriptor parameter in parameters)
                {
                    //从请求中提取参数的值
                    var argument = context.ActionArguments[parameter.ParameterName];
                    EvaluateValidationAttributes(parameter, argument, context.ModelState);
                }
            }
            //判断验证是否通过
            if (!context.ModelState.IsValid)
            {
                //返回自定义信息
                var result = string.Join("; ", context.ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage));
                context.Response = context.Request.CreateResponse(HttpStatusCode.OK, result);
            }
            base.OnActionExecuting(context);
        }

        private void EvaluateValidationAttributes(HttpParameterDescriptor parameter, object argument, ModelStateDictionary modelState)
        {
            //从当前参数中提取它所有的ValidationAttribute特性
            IList<ValidationAttribute> validationAttributes = parameter.GetCustomAttributes<ValidationAttribute>();

            foreach (var attributeData in validationAttributes)
            {
                if (attributeData != null)
                {
                    //使用特性对参数值验证
                    var isValid = attributeData.IsValid(argument);
                    if (!isValid)
                    {
                        //验证失败后将错误信息写入ModelState
                        modelState.AddModelError(parameter.ParameterName, attributeData.FormatErrorMessage(parameter.ParameterName));
                    }
                }
            }
        }
    }
}

返回结果:

解决思路就是在方法执行前主动获取请求的参数,并取得参数中有关ValidationAttribute特性的集合,并手动验证,最后将错误信息添加至HttpActionContext.ModelState,这样即可解决WebApi或者Asp.Net MVC中ValidationAttribute无法在参数上生效的问题。

感谢:https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/

.
.
.
.
.
.
.
.
.

【本文章出自NM1024.com,转载请注明作者出处。】






>>转载请注明原文链接地址:WebApi或Asp.Net MVC中参数验证无效