_controller_validate

controller 中我们首先对所有的请求进行日志记录,身份校验,参数校验之后直接把过滤后的数据丢给 logic(serviceImpl) 。这样子一来 controller 只是充当了 路由 + 过滤器 的作用,如果之后修改 API ,前端的请求地址不需要修改,只要在 logic(serviceImpl) 中修改或者重新一个方法就行了。

springboot 前端参数校验 1 2 3

在 Spring Boot 项目中,参数校验是一个非常重要的环节,主要使用 JSR-303/JSR-380 规范的实现(如 Hibernate Validator)来完成。在Spring Boot中,前端参数校验通常通过 JSR 380(Bean Validation 2.0) 实现,结合 Hibernate Validator(默认实现)和 Spring 的自动配置,可以实现对前端传递参数的校验。以下是如何在 Spring Boot 中实现前端参数校验的详细步骤:


1. 添加依赖

pom.xml 文件中添加 spring-boot-starter-validation 依赖:

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

在 Gradle 构建工具中添加 spring-boot-starter-validation 依赖:

implementation 'org.springframework.boot:spring-boot-starter-validation'  

2. 创建 DTO 类并添加校验注解

使用 JSR 380 提供的注解对字段进行校验,例如 @NotNull, @Size, @Email, @Pattern 等。

示例:用户数据校验
import javax.validation.constraints.*;

public class UserDTO {

    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3到20个字符之间")
    private String username;

    @NotNull(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 18, message = "年龄必须大于或等于18岁")
    @Max(value = 100, message = "年龄必须小于或等于100岁")
    private Integer age;

    @Pattern(regexp = "^[0-9]+$", message = "电话号码只能包含数字")
    private String phone;

    // Getters and Setters
}

3. 在 Controller 中使用 @Valid 注解

在控制器中,使用 @Valid 注解对请求体进行校验。如果校验失败,Spring 会自动抛出 MethodArgumentNotValidException

示例:控制器方法
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO) {
        // 如果校验通过,执行逻辑
        return ResponseEntity.ok("用户创建成功: " + userDTO.getUsername());
    }
}

4. 全局异常处理

为了返回更友好的错误信息,可以通过 @ControllerAdvice / @RestControllerAdvice @ExceptionHandler 捕获校验异常并自定义返回格式。

示例:全局异常处理器

使用@ControllerAdvice和@ExceptionHandler来处理MethodArgumentNotValidException以返回自定义错误消息:

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}

使用@RestControllerAdvice和@ExceptionHandler来处理MethodArgumentNotValidException和ConstraintViolationException以返回自定义错误消息:

import org.springframework.web.bind.annotation.ExceptionHandler;  
import org.springframework.web.bind.annotation.RestControllerAdvice;  
import org.springframework.validation.BindException;  
import org.springframework.validation.FieldError;  
import javax.validation.ConstraintViolationException;  
import java.util.HashMap;  
import java.util.Map;  

@RestControllerAdvice  // 等同于 @ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {  

    // 处理 @RequestBody 的 Bean (dto) 校验失败; @Valid
    // 常用于 JSON (dto) 的输入校验
    @ExceptionHandler(MethodArgumentNotValidException.class)  
    public ResponseEntity<Map<String, String>> handleValidationExceptions(  
            MethodArgumentNotValidException ex) {  
        Map<String, String> errors = new HashMap<>();  
        ex.getBindingResult().getAllErrors().forEach((error) -> {  
            String fieldName = ((FieldError) error).getField();  
            String errorMessage = error.getDefaultMessage();  
            errors.put(fieldName, errorMessage);  
        });  
        return ResponseEntity.badRequest().body(errors);  
    }  
    
    // controller 方法参数或方法返回值验证失败时抛出; @Validated, 参数校验
    // @PathVariable, @RequestParam 或返回值校验
    @ExceptionHandler(ConstraintViolationException.class)  
    public ResponseEntity<Map<String, String>> handleConstraintViolation(  
            ConstraintViolationException ex) {  
        Map<String, String> errors = new HashMap<>();  
        ex.getConstraintViolations().forEach(violation -> {  
            String fieldName = violation.getPropertyPath().toString();  
            String errorMessage = violation.getMessage();  
            errors.put(fieldName, errorMessage);  
        });  
        return ResponseEntity.badRequest().body(errors);  
    }  
}  

5. 配置校验规则(可选)

如果需要自定义校验规则,可以实现 ConstraintValidator 接口。

示例:自定义校验注解
创建注解
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPhone {
    String message() default "电话号码格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
创建校验逻辑
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 校验逻辑:电话号码只能包含数字,且长度为10-15
        return value != null && value.matches("^[0-9]{10,15}$");
    }
}
使用自定义注解
public class UserDTO {

    @ValidPhone
    private String phone;

    // 其他字段
}

6. 示例请求与响应

请求示例
// 使用 axios 发送请求  
async function createUser(userData) {  
    try {  
        const response = await axios.post('/api/users', userData);  
        console.log('创建成功:', response.data);  
    } catch (error) {  
        if (error.response && error.response.data) {  
            // 处理验证错误  
            const validationErrors = error.response.data;  
            Object.keys(validationErrors).forEach(field => {  
                console.error(`${field}: ${validationErrors[field]}`);  
            });  
        }  
    }  
}  
正确请求
{
  "username": "JohnDoe",
  "email": "john.doe@example.com",
  "age": 25,
  "phone": "1234567890"
}
错误请求
{
  "username": "JD",
  "email": "invalid-email",
  "age": 15,
  "phone": "abc123"
}
错误响应示例
{
  "username": "用户名长度必须在3到20个字符之间",
  "email": "邮箱格式不正确",
  "age": "年龄必须大于或等于18岁",
  "phone": "电话号码格式不正确"
}

常用校验注解

注解功能说明
@NotNull验证字段不能为空
@NotEmpty验证字段不能为空且长度大于0(字符串), 不能为空(字符串长度不为0或集合大小不为0)
@NotBlank验证字段不能为空且必须包含非空白字符, 不能为空(去除首尾空格后长度不为0)
@Size验证字符串、集合等的长度或大小范围
@Min / @Max验证数字的最小值和最大值
@Email验证邮箱格式
@Pattern使用正则表达式验证字段
@Digits验证数字的整数位数和小数位数
@Past / @Future验证日期必须是过去或未来
@Positive正数
@Negative负数

总结

通过 Spring Boot 的 spring-boot-starter-validation 和 Hibernate Validator,可以轻松实现前端参数校验。结合注解、全局异常处理器和自定义校验规则,可以满足大多数场景的需求,确保数据的完整性和正确性。

以上内容提供了一个完整的 Spring Boot 参数校验方案。主要包括:

  1. 依赖配置:添加必要的验证依赖
  2. 实体类验证:使用注解定义验证规则
  3. 控制器验证:使用 @Valid@Validated 开启验证
  4. 异常处理:统一处理验证异常
  5. 常用注解说明:列举了常用的验证注解
  6. 前端调用示例:展示了如何处理验证结果

一些重要说明:

  1. @Valid@Validated 的区别:
    • @Valid 是 JSR-303 规范的标准注解
    • @Validated 是 Spring 提供的注解,支持分组校验
  2. 嵌套验证:
    • 对象中包含其他对象时,需要在字段上添加 @Valid 注解
  3. 分组验证:
    • 可以通过定义接口来实现不同场景下的验证规则
  4. 自定义验证:
    • 可以通过 @Constraint 注解创建自定义验证规则

@Valid @Validated

在Spring Boot中,@Valid@Validated 是两种用于触发参数校验的注解,它们都可以用来验证前端传递的参数,但它们有一些细微的区别和适用场景。以下是对两者的详细对比和使用说明:


1. @Valid

@ValidJSR 380(Bean Validation 2.0) 规范的一部分,由 Java 提供,通常用于触发 Hibernate Validator 的校验逻辑。

特点
  • 来源javax.validation.Valid
  • 作用范围
    • 用于校验 Java Bean(如 DTO 对象)。
    • 可以嵌套校验(即校验对象中的嵌套对象)。
    • 不支持分组验证
    • 可以用在方法参数和字段
  • 分组校验:不支持分组校验(需要用 @Validated)。
  • 常用场景:用于校验简单的请求参数或嵌套对象。
示例
校验简单对象
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class UserDTO {

    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3到20个字符之间")
    private String username;

    @NotNull(message = "邮箱不能为空")
    private String email;

    // Getters and Setters
}
在控制器中使用 @Valid
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public String createUser(@Valid @RequestBody UserDTO userDTO) {
        return "用户创建成功: " + userDTO.getUsername();
    }
}

2. @Validated

@Validated 是 Spring 提供的注解,功能更加强大,除了支持基本的校验功能外,还支持 分组校验

特点
  • 来源org.springframework.validation.annotation.Validated
  • 作用范围
    • 用于校验 Java Bean(如 DTO 对象)。
    • 支持分组校验。
    • 可以用在类型、方法和方法参数
    • 不支持嵌套验证(除非结合@Valid)
  • 分组校验:支持分组校验(通过 groups 属性指定校验分组)。
  • 常用场景:用于需要分组校验的复杂场景。
示例
分组校验
定义校验分组
public interface CreateGroup {}
public interface UpdateGroup {}
在 DTO 中指定分组
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class UserDTO {

    @NotNull(groups = CreateGroup.class, message = "用户名不能为空")
    @Size(min = 3, max = 20, groups = {CreateGroup.class, UpdateGroup.class}, message = "用户名长度必须在3到20个字符之间")
    private String username;

    @NotNull(groups = UpdateGroup.class, message = "邮箱不能为空")
    private String email;

    // Getters and Setters
}
在控制器中使用 @Validated
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

    // 创建用户时校验 CreateGroup 分组
    @PostMapping
    public String createUser(@Validated(CreateGroup.class) @RequestBody UserDTO userDTO) {
        return "用户创建成功: " + userDTO.getUsername();
    }

    // 更新用户时校验 UpdateGroup 分组
    @PutMapping
    public String updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
        return "用户更新成功: " + userDTO.getUsername();
    }
}

3. @Valid 和 @Validated 的区别

特性@Valid@Validated
来源javax.validation.Validorg.springframework.validation.annotation.Validated
分组校验不支持支持
嵌套校验支持支持
适用场景简单校验复杂校验(需要分组时)
校验触发位置由 Bean Validation 规范触发由 Spring 的校验机制触发

4. 嵌套校验

无论是 @Valid 还是 @Validated,都支持嵌套校验。只需在嵌套对象的字段上添加 @Valid 注解即可。

示例
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

public class UserDTO {  
    @NotNull(message = "用户名不能为空")  
    private String username;  
    
    @NotNull(message = "地址不能为空")  
    private String address;  
    
    // getters and setters  
}  

public class OrderDTO {

    @NotNull(message = "订单ID不能为空")
    private String orderId;

    @Valid // 必须加@Valid才能验证嵌套对象 UserDTO
    @NotNull(message = "用户信息不能为空")
    private UserDTO user;

    // Getters and Setters
}

在控制器中:

@PostMapping("/orders")
public String createOrder(@Valid @RequestBody OrderDTO orderDTO) {
    return "订单创建成功";
}

方法级别验证

@Service  
@Validated  // 类级别需要添加@Validated  
public class UserService {  
    
    public void createUser(@NotNull(message = "用户名不能为空") String username,  
                          @Min(value = 0, message = "年龄不能为负数") int age) {  
        // 处理逻辑  
    }  
    
    @Validated(Create.class)  // 方法级别的分组验证  
    public void saveUser(@Valid UserDTO userDTO) {  
        // 处理逻辑  
    }  
}  

5. 总结

  • @Valid

    • 用于简单校验。
    • 支持嵌套校验。
    • 不支持分组校验。
    • 更加通用,适用于大多数场景。
  • @Validated

    • 支持分组校验。
    • 适用于复杂校验场景(如不同操作需要不同的校验规则)。
    • 是 Spring 提供的扩展功能。

在实际开发中,如果不需要分组校验,@Valid 足够满足需求;如果需要分组校验,则必须使用 @Validated

6. 建议

  1. Controller层验证
    • 使用@Valid进行基础的请求体验证
    • 如果需要分组验证,使用@Validated
  2. Service层验证
    • 使用@Validated在类级别开启验证
    • 使用参数级别的约束注解进行验证
  3. 嵌套验证
    • 需要验证嵌套对象时,确保使用@Valid
  4. 分组验证
    • 当同一个DTO在不同场景下有不同的验证规则时,使用@Validated配合验证分组
  5. 异常处理
    • 确保同时处理MethodArgumentNotValidExceptionConstraintViolationException

通过合理使用这两个注解,可以实现更灵活和强大的参数验证功能。

参考:


  1. https://blog.csdn.net/weixin_45541665/article/details/139411351 ↩︎

  2. https://blog.csdn.net/Zyw907155124/article/details/139414414 ↩︎

  3. https://www.bilibili.com/video/BV1824y1f7tJ/?vd_source=d17e678df6de64f39874a3291030831b ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值