Asp.Net Core模型验证

【微软模型校验官网】

重点

1.传统MVC框架下需要手动触发模型验证

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }
}

2.WebApi的模型校验通常指在数据进入指定的Action前,进行的一次模型校验。

3.不是模型上加上特性就属于模型验证

4.在ViewModel,Dto,Model的模型分层的情况下。ViewModel负责接收前端的传递参数。那么在ViewModel上添加校验特性才会触发默认的模型校验。


【注意区分 ORM(EF,sqlsuger )框架模型类上的特性 与ViewModel上的特性的区别】

 

常规套路

内置特性

以下是一些内置验证特性:

  • [CreditCard]:验证属性是否具有信用卡格式。
  • [Compare]:验证模型中的两个属性是否匹配。
  • [EmailAddress]:验证属性是否具有电子邮件格式。
  • [Phone]:验证属性是否具有电话号码格式。
  • [Range]:验证属性值是否在指定的范围内。
  • [RegularExpression]:验证属性值是否与指定的正则表达式匹配。
  • [Required]:验证字段是否不为 null。 有关此属性的行为的详细信息。
  • [StringLength]:验证字符串属性值是否不超过指定长度限制。
  • [Url]:验证属性是否具有 URL 格式。
  • [Remote]:通过在服务器上调用操作方法来验证客户端上的输入。 

模型

public class Persion
    {
        [Required]
        public int Id { get; set; }

        [MaxLength(10,ErrorMessage ="最大长度为10")]
        [MinLength(2)]
        public string Name { get; set; }

        [Range(0,3)]
        public int Sex { get; set; }
    }

模型状态

[Route("[controller]")]
public class ModelController : ControllerBase
{
        [HttpPost(template: "Persion01")]
        public IActionResult TestPersion01([FromBody]Persion persion)
        {
            return Ok();
        }
}

模型绑定和验证都在执行控制器操作或 Razor 页面处理程序方法之前发生。 Web 应用负责检查 ModelState.IsValid 并做出相应响应。 Web 应用通常会重新显示带有错误消息的页面:

如果 Web API 控制器具有 [ApiController] 特性,则它们不必检查 ModelState.IsValid。 在此情况下,如果模型状态无效,将返回包含错误详细信息的自动 HTTP 400 响应。

请求以及返回结果

我们可以看出在Id不传的情况下,没有报Id的错误。这是因为aspnet core默认的json序列化器在我们id为int型(不可为空类型)时。在不传Id参数的时候会将Id初始化为默认值也就是int默认值0

在Sex传的字符数大于10的时候传出的错误是我们[MaxLength(10,ErrorMessage ="最大长度为10")]特性的ErrorMessage ,因此我们可以自定义我们要返回的错误信息。

Sex字段我们早模型特性上没有写ErrorMessage 所以会抛出默认的提示信息。

以上就是微软自带的默认的模型验证方式的常规使用套路。但是这能达到我们的现实项目的基本需求么?那我们接着看

需求

在我们的实际的前后端分离项目开发过程中,我们通常会把我们返回的类型做一次统一的模型处理,通常会封装成一个统一的类来做返回值。这样的好处是

1.风格统一,便于管理。

2.我们可以抓取错误信息,做统一的处理。类似400,401,500的状态码我们可以把这些状态码更改为200。然后返回我们统一格式的模型(虽然我觉得不应该推荐这样做)返回我们想要返回的错误信息。这样的好处是前端怎么都能接收到200状态码。且能做统一的错误处理(类似弹框,弹出我们的错误信息)

思考

1.在我们上述的需求中,我们用我们上面的模型验证还是达不到我们想要的结果。模型验证的结果我们还无法控制返回的结构是我们自定义的统一给前端返回的格式。

2.我们也没有做状态转码(这里专注于模型验证,暂时不阐述)

代码

简单的统一返回模型

 public class Reponse<T> 
    {
        
        /// <summary>
        /// 数据
        /// </summary>
        public T Data { get; set; }

        /// <summary>
        /// 状态码
        /// </summary>
        public int State { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrMessage { get; set; }
    }

    public class Reponse
    {
        /// <summary>
        /// 状态码
        /// </summary>
        public int State { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrMessage { get; set; }
    }

模型状态

[ApiController]
    [Route("[controller]")]
    public class ModelController : ControllerBase
    {
        [HttpPost(template: "Persion01")]
        public IActionResult TestPersion01([FromBody]Persion persion)
        {
            //....逻辑处理
            return Ok(new Reponse<Persion> { State=1,Data= persion });
        }
    }

好的下面就是我的解决方案(敲敲黑板知识点)

在SetUp类的ConfigureServices方法上

services.AddControllers()
                .ConfigureApiBehaviorOptions(
                option => {
                    //抑制模型验证(人话就是关闭模型验证)
                    //option.SuppressModelStateInvalidFilter = true;
                    option.InvalidModelStateResponseFactory = action =>{
                        var IsValid = action.ModelState.IsValid;
                        //var Context = action.HttpContext;
                        if (!IsValid)
                        {
                            var errorMsg = action.ModelState.Values
                                                            .SelectMany(e => e.Errors)
                                                            .Select(e => e.ErrorMessage);

                            var errorMsgString = "";

                            if (errorMsg.Any())
                            {
                                foreach (var errorMsgitem in errorMsg)
                                {
                                    errorMsgString += errorMsgitem;
                                }

                                Reponse reponse = new Reponse() { 
                                    ErrMessage = errorMsgString
                                    , State = 0 };

                                var reponseString= JsonConvert.SerializeObject(reponse);

                                action.HttpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(reponseString, 0, reponseString.Length));

                                return new BadRequestResult();
                                //return new OkObjectResult(reponse);

                            }
                        }
                          return new BadRequestResult();
                      };
                }) ;

这个我看到很多的博客的做法是抑制默认的模型,然后添加自己的模型验证Filter。虽然模型验证自身也就是一个Filter但是个人感觉这么做总有点怪怪的。不恰当的比喻就是你和室友俩人上食堂打个饭,俩人都打了一个蛋,到吃的时候你非得要你室友的蛋。理由是他打的蛋长得好看。为什么这么说呢,因为Aspnet Core模型验证的处理方法已经暴露给你了,你就不要他的非得自己写。

那上面的方法就是直接在微软提供的模型验证的环节进行模型验证。 我觉得这样才是最推荐的方式(个人推荐:这种方式无法满足你的需求再写自己的Filter做模型验证)

上面的方法会在模型验证失败时才会接收到

演示结果

 

posted on 2020-07-04 20:37  盆栽三巡  阅读(815)  评论(0)    收藏  举报

导航