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;
}
相关推荐
叫我阿柒啊3 小时前
从Java全栈到云原生:一场技术深度对话
java·spring boot·docker·微服务·typescript·消息队列·vue3
计算机毕设定制辅导-无忧学长4 小时前
MQTT 与 Java 框架集成:Spring Boot 实战(一)
java·网络·spring boot
叫我阿柒啊4 小时前
从Java全栈到Vue3实战:一次真实面试的深度复盘
java·spring boot·微服务·vue3·响应式编程·前后端分离·restful api
泉城老铁4 小时前
Spring Boot中实现多线程分片下载
java·spring boot·后端
泉城老铁4 小时前
Spring Boot中实现多文件打包下载
spring boot·后端·架构
泉城老铁4 小时前
Spring Boot中实现大文件分片下载和断点续传功能
java·spring boot·后端
友莘居士4 小时前
长流程、复杂业务流程分布式事务管理实战
spring boot·rocketmq·saga·复杂流程分布式事务·长流程
百思可瑞教育4 小时前
Spring Boot 参数校验全攻略:从基础到进阶
运维·服务器·spring boot·后端·百思可瑞教育·北京百思教育
小蒜学长5 小时前
spring boot驴友结伴游网站的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
一个松13 小时前
【无标题】
spring boot