【springboot框架增强】优雅的自定义参数校验详解 全网最新

328 阅读5分钟

一、前言

1. 传统参数校验的不足

在上一篇文章中,已经详细的讲解了自定义全局异常捕获,今天接着写参数校验相关的。在日常的项目中,我们可能忽略了参数校验的重要性,参数校验可以限制用户的输入,提高系统的安全性,使程序更加的健壮。在日常的项目中,我们可能直接在controller层做参数校验,比如发现某个必传参数前端没有传过来,然后后端判断一下,发现为空,直接返回给前端参数缺失

在我们日常开发中,数据校验的实现是一件比较痛苦的事情,繁琐且无趣,对于一般的业务而言,极少出现一些过于复杂的校验,常常都是非空,长度,最大最小值,正则,数据关联,定值等等。我在开发过程中为了减少我写这些代码的coding成本,由此认识了javax.validation包下的 @Valid 注解 和 Java 的 Bean Validation 规范。

如果按照规范,controller层不应该放参数判断相关的逻辑,同时如果参数非常多,都需要判断,那么将会非常的麻烦,那么如何优雅的解决这个问题呢?

2. 解决方案

我们其实可以通过自定义异常捕获+springboot自带的validation来解决这个问题,在处理参数的时候配合异常处理,只要加几个注解就能轻松搞定参数校验,自定义异常捕获在上一篇文章中已经详细讲到,没有看过的可以看我的上一篇文章地址是【springboot框架增强】优雅的自定义异常捕获 全网最新

下面我们一起来看学习参数校验吧

在这里插入图片描述

二、几种传递参数的形式

1. Get请求通过拼接url进行传递

相当于把参数拼接在url后面,然后在controller方法上加上@PathVariable注解,参数要和{}中的名称对应上。

举例子:

    @GetMapping("getUserByOpenid/{openid}")	
    @CustomizeLog
    public ReturnMsgEntity getUserByOpenid(@PathVariable @NeedAttention String openid) {
		return null;
   }

@CustomizeLog和@NeedAttention注解不需要管,下篇文章会详细讲解自定义注解的使用方法

这个时候我们访问路径的形式如下:

http://localhost:8090/user/getUserByOpenid/1


2. Get请求通过?拼接参数

查询参数

举例子:

   @GetMapping("getUserById")
    public ReturnMsgEntity findUserById(@RequestParam(value = "id", required = false)
                                       Integer id) {
	return null;
}

3. Post请求传递Body参数

使用@RequestBody 注解。 举例子:

    @PostMapping("save")
    public ReturnMsgEntity save(@RequestBody @Validated WxuserDTO wxuserDTO) {
		return null;
	}

以上简单的介绍了参数传递的形式,下面开始介绍如何利用注解进行参数校验。

三、使用javax.validation.constraints的一些注解,进行参数校验

1. JSR(Java Specification Requests)

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

2. JSR-303

是Java EE 6 中的一项子规范,叫做Bean Validation,hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

通俗易懂的讲,Bean Validation的参数校验机制是JSR-303的一种实现方式。 里面有大概有这些注解: 在这里插入图片描述

3. 使用注解的方法

3.1 get请求的使用方法

  1. 在类上加Validated注解
  2. 在方法请求参数前加注解和提示 举例子:
    @GetMapping("getUserById")
    public ReturnMsgEntity findUserById(@RequestParam(value = "id", required = false)
                                        @Max(value = 10, message = "id最大值不超过10")
                                        @NotNull(message = "id不能为空") Integer id) {
	return null;
}

请求方式:

http://localhost:8090/user/getUserById?id=11 结果: 在这里插入图片描述

上面的结果并不是我们想要的,我们想要我们自己定义的,这时候就需要自定义处理异常了。 首先看报错信息: 在这里插入图片描述 我们要捕获ConstraintViolationException异常,同时获取到里面的信息,封装成我们自己的ReturnMsgEntity,接着上篇文章点我,自定义异常捕获链接,具体代码如下:

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ReturnMsgEntity handlerConstraintException(HttpServletRequest request, ConstraintViolationException e) {
        String requestUrl = request.getRequestURI();
        String method = request.getMethod();
//        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
//            constraintViolation.getMessage();
//        }
        String messages = e.getMessage();
        return new ReturnMsgEntity(10001, messages, null, method + "  " + requestUrl);
    }

这时候我们再请求,就是我们想要的了 在这里插入图片描述

3.1 post请求的使用方法

  1. 在参数上要加@Validated注解
  2. 在实体类的属性上就可以加各种注解了
  3. 如果一个实体类中又嵌套实体类,需要在第一个实体类中定义第二个实体类的属性上加上@Valid注解。 在这里插入图片描述 在这里插入图片描述

和get方法类似,也是捕获异常,在GlobalExceptionAdvice类中新增

@ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ReturnMsgEntity handlerBeanValidation(HttpServletRequest request, MethodArgumentNotValidException e) {
        String requestUrl = request.getRequestURI();
        String method = request.getMethod();
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String messages = formatAllErrorMessages(errors);
        return new ReturnMsgEntity(10001, messages, null, method + "  " + requestUrl);
    }


    /**
     * 处理?里的参数校验
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ReturnMsgEntity handlerMissingServletRequestParameter(HttpServletRequest request, MissingServletRequestParameterException e) {
        String requestUrl = request.getRequestURI();
        String method = request.getMethod();
//        List<ObjectError> errors = e.getBindingResult().getAllErrors();
//        String messages = formatAllErrorMessages(errors);
        return new ReturnMsgEntity(10001, "错误", null, method + "  " + requestUrl);
    }


    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ReturnMsgEntity handlerConstraintException(HttpServletRequest request, ConstraintViolationException e) {
        String requestUrl = request.getRequestURI();
        String method = request.getMethod();
//        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
//            constraintViolation.getMessage();
//        }
        String messages = e.getMessage();
        return new ReturnMsgEntity(10001, messages, null, method + "  " + requestUrl);
    }

    private String formatAllErrorMessages(List<ObjectError> errors) {
        StringBuffer errorMsg = new StringBuffer();
        errors.forEach(error -> {
            errorMsg.append(error.getDefaultMessage()).append(";");
        });
        return errorMsg.toString();

    }

结果: 在这里插入图片描述

四、封装message

前几步已经完成了自定义校验,但提示信息都存放硬编码在类中,有没有方法把信息都存在配置文件中呢?

当然有,我们可以通过在resource下新建ValidationMessages.properties,从而达到目的。

1. ValidationMessages.properties配置文件的创建

在resource下建立ValidationMessages.properties,同时根据业务定义一些消息提示

在这里插入图片描述


四、 结尾

以上大概是参数校验的详细讲解,可以解决大部分问题,但是如果是很复杂的校验该怎么办呢?篇幅有限,下一篇来讲解自定义注解,来解决复杂校验,以及自定义注解的一些场景举例。