• 寻找写代码感觉(十六)之 集成Validation做参数校验


    一、写在前面

    今天是大年初五了......

    不知不觉,又要上班了,美好的假期只剩一天了,有点不舍呢!

    也不知道为什么,总感觉像没睡醒一样,也不是因为眼睛小,更多应该是自寻烦恼,想得多罢了。

    二、参数校验任务拆解

    • 对保存接口和查询接口增加参数校验
    • 校验不通过时,前端弹出错误提示

    三、集成Validation做参数校验实现

    1、引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    
    

    2、对查询接口增加参数校验

    2.1、给对应实体字段添加校验,示例代码如下:

    
    package com.rongrong.wiki.req;
    
    import lombok.Data;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.NotNull;
    
    @Data
    public class PageReq {
        private int page;
        @NotNull(message = "【每页条数】不能为空")
        @Max(value = 100, message = "【每页条数】不能超过100")
        private int size;
    }
    

    2.2、再给对应接口添加校验,示例代码如下:

       /**
         * 查询功能,支持按名字模糊查询
         *
         * @param eBookReq
         * @return
         */
        @GetMapping("/list")
        public CommonResp list(@Valid EBookQueryReq eBookReq) {
            CommonResp<PageResp<EBookResp>> resp = new CommonResp<>();
            PageResp<EBookResp> list = eBookService.list(eBookReq);
            resp.setMessage("执行查询成功!");
            resp.setContent(list);
            return resp;
        } 
    

    2.3、测试接口是否有校验了

    GET http://localhost:8888/ebook/list?page=1&size=1000
    
    HTTP/1.1 400 
    Vary: Origin
    Vary: Access-Control-Request-Method
    Vary: Access-Control-Request-Headers
    Content-Type: application/json
    Transfer-Encoding: chunked
    Date: Sat, 05 Feb 2022 01:26:51 GMT
    Connection: close
    
    {
      "timestamp": "2022-02-05T01:26:51.739+00:00",
      "status": 400,
      "error": "Bad Request",
      "trace": "org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'EBookQueryReq' on field 'size': rejected value [1000]; codes [Max.EBookQueryReq.size,Max.size,Max.int,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [EBookQueryReq.size,size]; arguments []; default message [size],100]; default message [【每页条数】不能超过100]\r\n\tat org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:170)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n",
      "message": "Validation failed for object='EBookQueryReq'. Error count: 1",
      "errors": [
        {
          "codes": [
            "Max.EBookQueryReq.size",
            "Max.size",
            "Max.int",
            "Max"
          ],
          "arguments": [
            {
              "codes": [
                "EBookQueryReq.size",
                "size"
              ],
              "arguments": null,
              "defaultMessage": "size",
              "code": "size"
            },
            100
          ],
          "defaultMessage": "【每页条数】不能超过100",
          "objectName": "EBookQueryReq",
          "field": "size",
          "rejectedValue": 1000,
          "bindingFailure": false,
          "code": "Max"
        }
      ],
      "path": "/ebook/list"
    }
    
    Response code: 400; Time: 105ms; Content length: 5763 bytes
    

    由上可知,已经触发了校验,但这样的提示,并不是我们想要的,需要对校验做进一步处理。

    2.4、统一异常处理

    加入后,将会对controller中的异常进行处理,示例代码如下:

    package com.rongrong.wiki.controller;
    
    import com.rongrong.wiki.exception.BusinessException;
    import com.rongrong.wiki.resp.CommonResp;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.validation.BindException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * 统一异常处理、数据预处理等
     */
    @ControllerAdvice
    public class ControllerExceptionHandler {
    
        private static final Logger LOG = LoggerFactory.getLogger(ControllerExceptionHandler.class);
    
        /**
         * 校验异常统一处理
         * @param e
         * @return
         */
        @ExceptionHandler(value = BindException.class)
        @ResponseBody
        public CommonResp validExceptionHandler(BindException e) {
            CommonResp commonResp = new CommonResp();
            LOG.warn("参数校验失败:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            commonResp.setSuccess(false);
            commonResp.setMessage(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            return commonResp;
        }
    
        /**
         * 校验异常统一处理
         * @param e
         * @return
         */
        @ExceptionHandler(value = BusinessException.class)
        @ResponseBody
        public CommonResp validExceptionHandler(BusinessException e) {
            CommonResp commonResp = new CommonResp();
            LOG.warn("业务异常:{}", e.getCode().getDesc());
            commonResp.setSuccess(false);
            commonResp.setMessage(e.getCode().getDesc());
            return commonResp;
        }
    
        /**
         * 校验异常统一处理
         * @param e
         * @return
         */
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public CommonResp validExceptionHandler(Exception e) {
            CommonResp commonResp = new CommonResp();
            LOG.error("系统异常:", e);
            commonResp.setSuccess(false);
            commonResp.setMessage("系统出现异常,请联系管理员");
            return commonResp;
        }
    }
    
    

    再次调用查询接口,测试结果如下:

    3、对保存接口增加参数校验

    和上面的查询接口一样,也是需要将需要校验字段加上注解校验,再对接口加上对应注解,即完成校验操作,示例代码如下:

    
        @NotNull(message = "【名称】不能为空")
        private String name;
    
        /**
         * 新增或编辑功能
         *
         * @param eBookSaveReq
         * @return
         */
        @PostMapping("/save")
        public CommonResp save(@Valid @RequestBody EBookSaveReq eBookSaveReq) {
            CommonResp resp = new CommonResp<>();
            eBookService.save(eBookSaveReq);
            return resp;
        }
    
    
    

    4、校验不通过时,前端弹出错误提示

    在前段页面添加弹窗提示组件,示例代码如下:

    import {message} from 'ant-design-vue';
    
          if (data.success) {
              modalVisible.value = false;
              modalLoading.value = false;
              //重新加载列表
              handleQuery({
                page: 1,
                size: pagination.value.pageSize,
              });
            }else {
              message.error(data.message);
            }
          })
    
    

    四、编译运行,测试结果

    1、查询接口前端验证结果

    2、保存接口前端验证结果

    五、写在最后

    就一个功能来说的话,肯定是要有对应的校验规则,才是严谨的,从某种角度来说,也体现了程序的健壮性。

    当然,也可能有的同学会说,写校验规则多麻烦,基本功能能用不就行了吗。

    其实不然,因为用户的行为,是最不可控的,不能保证每个使用者(用户),都能按照开发者预设的想法去使用软件。

    如果那样话,那所有程序,都会没有bug了,你说是不是?

  • 相关阅读:
    10、函数介绍、函数参数、函数的返回值
    9、bytes类型,文件处理
    8、集合类型、文件处理
    7、列表类型、元组、字典类型
    6、for循环补充、可变类型与不可变类型、基本操作
    5、while循环与for循环
    4、基本运算符、if语法
    3、变量、常量、基本数据类型
    爬虫(一)
    小技能(二)
  • 原文地址:https://www.cnblogs.com/longronglang/p/15864014.html
Copyright © 2020-2023  润新知