3.9参数校验

分类: 搭建单体商城服务

参数校验

参数校验是保证数据有效性的重要手段。Spring Boot 提供了 Bean Validation 支持,可以方便地实现参数校验。本节将学习如何实现参数校验。

本节将学习:@Valid 注解、@NotNull、@NotBlank、@Size、自定义校验器,以及校验错误处理。

@Valid 注解

@Valid 使用

@Valid 注解用于触发参数校验,通常与 @RequestBody 一起使用。

依赖添加

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

基本使用

@PostMapping("/users") public Result<User> createUser(@RequestBody @Valid User user) { User created = userService.register(user); return Result.success(created); }

常用校验注解

@NotNull、@NotBlank、@NotEmpty

package com.example.ecommerce.entity; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; @Data public class User { @NotNull(message = "ID cannot be null") private Long id; @NotBlank(message = "Username cannot be blank") private String username; @NotBlank(message = "Email cannot be blank") private String email; @NotBlank(message = "Password cannot be blank") private String password; }

@Size

@NotBlank(message = "Username cannot be blank") @Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters") private String username; @NotBlank(message = "Password cannot be blank") @Size(min = 8, max = 20, message = "Password must be between 8 and 20 characters") private String password;

@Email

@NotBlank(message = "Email cannot be blank") @Email(message = "Email format is invalid") private String email;

@Pattern

@NotBlank(message = "Phone cannot be blank") @Pattern(regexp = "^1[3-9]\\d{9}$", message = "Phone format is invalid") private String phone;

校验注解列表

自定义校验器

自定义注解

package com.example.ecommerce.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PhoneValidator.class) @Documented public @interface Phone { String message() default "Phone format is invalid"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }

校验器实现

package com.example.ecommerce.validation; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class PhoneValidator implements ConstraintValidator<Phone, String> { private static final String PHONE_PATTERN = "^1[3-9]\\d{9}$"; @Override public void initialize(Phone constraintAnnotation) { // 初始化 } @Override public boolean isValid(String phone, ConstraintValidatorContext context) { if (phone == null || phone.isEmpty()) { return true; // 由 @NotBlank 处理空值 } return phone.matches(PHONE_PATTERN); } }

使用自定义校验器

@Data public class User { @NotBlank(message = "Phone cannot be blank") @Phone(message = "Phone format is invalid") private String phone; }

校验错误处理

校验流程

异常处理

@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { String message = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); return Result.error(ResultCode.BAD_REQUEST, message); } @ExceptionHandler(BindException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Result<?> handleBindException(BindException e) { String message = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); return Result.error(ResultCode.BAD_REQUEST, message); } }

完整校验示例

实体类校验

package com.example.ecommerce.entity; import com.example.ecommerce.validation.Phone; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; @Data public class User { @NotNull(message = "ID cannot be null") private Long id; @NotBlank(message = "Username cannot be blank") @Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters") private String username; @NotBlank(message = "Email cannot be blank") @Email(message = "Email format is invalid") private String email; @NotBlank(message = "Phone cannot be blank") @Phone(message = "Phone format is invalid") private String phone; @NotBlank(message = "Password cannot be blank") @Size(min = 8, max = 20, message = "Password must be between 8 and 20 characters") private String password; }

Controller 使用

@RestController @RequestMapping("/api/users") public class UserController { @PostMapping public Result<User> createUser(@RequestBody @Valid User user) { User created = userService.register(user); return Result.success(created); } @PutMapping("/{id}") public Result<User> updateUser(@PathVariable Long id, @RequestBody @Valid User user) { user.setId(id); userService.updateById(user); return Result.success(user); } }

分组校验

校验分组

package com.example.ecommerce.validation; public interface CreateGroup {} public interface UpdateGroup {}

使用分组

@Data public class User { @NotNull(message = "ID cannot be null", groups = UpdateGroup.class) private Long id; @NotBlank(message = "Username cannot be blank", groups = {CreateGroup.class, UpdateGroup.class}) private String username; @NotBlank(message = "Password cannot be blank", groups = CreateGroup.class) private String password; }

Controller 使用分组

@PostMapping public Result<User> createUser(@RequestBody @Validated(CreateGroup.class) User user) { User created = userService.register(user); return Result.success(created); } @PutMapping("/{id}") public Result<User> updateUser(@PathVariable Long id, @RequestBody @Validated(UpdateGroup.class) User user) { user.setId(id); userService.updateById(user); return Result.success(user); }

官方资源

本节小结

在本节中,我们学习了:

第一个是 @Valid 注解。 使用 @Valid 触发参数校验。

第二个是常用校验注解。 @NotNull、@NotBlank、@Size、@Email、@Pattern 等。

第三个是自定义校验器。 创建自定义注解和校验器类。

第四个是校验错误处理。 使用全局异常处理器处理校验错误。

这就是参数校验。通过参数校验,我们可以确保请求参数的有效性,提高系统的健壮性。

在下一节,我们将学习如何实现用户模块,包括用户注册、登录等功能。