springboot学习第3期 - 参数校验

参数分为 路径参数、查询参数、请求体参数。

引入依赖

需要引入校验依赖:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

常用的校验注解包含:

注解 作用类型 说明
@NotNull 所有引用类型 对象不能为null
@NotEmpty String 字符串不能为空字符串
@NotBlank String 字符串不能为空格和空字符串
@Length(min=,max=) String 字符串的长度限制
@Size(min=, max=) 集合类型 集合类型的元素个数限制
@Min / @Max Integer 数字的大小限制
@Pattern(regexp="") String 字符串正则匹配
@Email String 字符串为邮件格式

路径参数

校验路径参数,除了变量前面加校验注解外,类上还需要加 @Validated 注解,

java 复制代码
@RestController
@RequestMapping("/api/hello")
@Validated
public class HelloController {

    @GetMapping("/{name}")
    public ApiResponse<String> hello(@PathVariable @Length(min = 2, max = 10) String name) {
        return ApiResponse.success("Hello World!" + name);
    }

}

postman 接口调试:

后端异常信息:

查询参数

查询参数和路径参数一样。

同样是变量前面添加校验注解,类上添加 @Validated 注解。

java 复制代码
@RestController
@RequestMapping("/api/hello")
@Validated
public class HelloController {

    @GetMapping
    public ApiResponse<String> hello(@RequestParam @Length(min = 2, max = 10) String name) {
        return ApiResponse.success("Hello World!" + name);
    }

}

查询参数封装

这种形式类上不需要加 @Validated。

封装的对象前面需要添加 @Valid 注解,这里也可以换成 @Validated 注解,如果需要分组校验,则使用 @Validated 注解,但实际开发中,很少会使用分组校验。

java 复制代码
@RestController
@RequestMapping("/api/hello")
public class HelloController {

    @GetMapping
    public ApiResponse<String> hello(@Valid Param param) {
        return ApiResponse.success("Hello World!" + param.getName());
    }

}
java 复制代码
@Data
public class Param {
    @Length(min = 2, max = 50)
    private String name;
    private String age;
}

postman接口调试:

后端服务器异常信息:

请求体参数

请求体参数的校验和查询参数封装一样的。

java 复制代码
@RestController
@RequestMapping("/api/hello")
public class HelloController {

    @PostMapping
    public ApiResponse<String> hello(@Valid @RequestBody Request request) {
        return ApiResponse.success("Hello World!" + request.getName());
    }
}

而封装的属性前面,添加具体的校验注解:

java 复制代码
@Data
public class Request {

    @Length(min = 2, max = 100)
    private String name;

    @JsonProperty("my_age")
    private String myAge;
}

使用postman调试接口:

服务端抛出的异常信息,抛出的异常是 MethodArgumentNotValidException:

包含自定义对象属性

请求体对象包含自定义对象 Person, 并且 Person 属性包含校验注解。

则Person变量前面需要添加 @Valid 注解。

java 复制代码
@Data
public class Request {

    @Length(min = 2, max = 100)
    private String name;

    @Valid   // 关键
    private Person person;

//  集合嵌套自定义对象,也是如此
//    @Valid
//    private List<Person> person;

//  或者这种形式也是可以的
// 	  private List<@Valid Person> person;
}
java 复制代码
@Data
public class Person {

    @Length(min = 4, max = 10)
    private String name;
}

集合普通元素

java 复制代码
@Data
public class Request {

    @Length(min = 2, max = 100)
    private String name;

    private List<@Length(min=2) String> tags;
}

tags 的每个元素都会应用 @Length(min=2) 注解校验。

全局异常处理

通过前面的postman接口调试看出,即使校验不通过,响应信息晦涩难懂,客户端根本不知道是因为什么而接口调不通。这次通过全局异常处理机制,优化校验不通过的提示信息。

通过前面三种参数的校验,可以知道如果校验不通过,会返回两种类型的异常,MethodArgumentNotValidException 和 ConstraintViolationException,主要在响应之前全局拦截这两个异常,将异常信息提取出来,友好的返回给客户端即可。

全局异常处理器使用注解 @RestControllerAdvice + @ExceptionHandler,当程序中抛出指定的异常信息,就会被拦截到,经过自定义处理,然后返回给客户端。

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<List<String>> handleValidException(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.toList());
        return ApiResponse.error(400, "参数校验失败", errors);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<List<String>> handleViolationException(ConstraintViolationException ex) {
        List<String> errors = ex.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList());
        return ApiResponse.error(400, "参数校验失败", errors);
    }
}

前面忘记提到,所有的校验注解都有个 message 属性, 当校验失败的时候,会提示 message 信息,而上述全局异常处理器,就是收集所有校验注解的message,返回给客户端

java 复制代码
@RestController
@RequestMapping("/api/hello")
@Validated
public class HelloController {

    @PostMapping("/{name}")
    public ApiResponse<String> hello(@PathVariable @Length(min = 2, max = 10, message = "Name length should be between 2 and 10") String name,
                                     @RequestParam @Min(value = 18, message = "Age should be between 0 and 100") String age) {
        return ApiResponse.success("Hello World!" + name);
    }

}

可以为参数校验失败设置特定的code, 比如 400001(400 + 001),然后前端响应拦截器判定到是400001,则展示所有的异常信息。关于响应码的设计,这是个大学问。

自定义校验注解

比如自定义一个校验电话号码的检验注解。

java 复制代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "手机号格式错误";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
java 复制代码
public class PhoneValidator implements ConstraintValidator<Phone, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches("^1[3-9]\\d{9}$");
    }
}

使用:

java 复制代码
@Data
public class Request {

    @Length(min = 2, max = 100, message = "name 长度应该在 2 到 100 之间")
    private String name;

    @Phone(message = "phone 格式不正确")
    private String phone;
}
相关推荐
livemetee1 分钟前
springboot 整合spring-kafka客户端:SASL_SSL+PLAINTEXT方式
spring boot·spring·kafka
midsummer_woo5 小时前
基于springboot+vue+mysql框架的工作流程管理系统的设计与实现(源码+论文+PPT答辩)
vue.js·spring boot·mysql
Q_Q19632884756 小时前
python的抗洪救灾管理系统
开发语言·spring boot·python·django·flask·node.js·php
Honesty8610246 小时前
深入排查:@Scope(“prototype“)与@RequestScope字段篡改问题全链路分析
java·spring boot·spring·原型模式
yangchanghua1116 小时前
Springboot 文件下载(Excel) + Vue前端下载按钮
前端·vue.js·spring boot·文件下载
凌康ACG6 小时前
springboot打包二次压缩Excel导致损坏
spring boot·后端·excel
麦兜*9 小时前
SpringBoot 3.x集成阿里云OSS:文件上传 断点续传 权限控制
java·spring boot·后端·spring·spring cloud·阿里云·maven
paopaokaka_luck16 小时前
基于SpringBoot+Uniapp球场预约小程序(腾讯地图API、Echarts图形化分析、二维码识别)
spring boot·小程序·uni-app
@ chen17 小时前
Spring Boot 解决跨域问题
java·spring boot·后端