JSR303参数校验

本文介绍了如何使用Java的校验注解(如@NotNull, @NotBlank等)和自定义分组进行数据校验,以及如何处理校验异常。通过开启校验功能并结合@Validated和@Valid,实现不同场景下的参数校验。同时,展示了如何创建自定义校验注解@ListValue及其验证器,以确保字段值属于预设的指定值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示

例如:

    @NotNull(message = "修改必须指定ID", groups = {UpdateGroup.class})
	@Null(message = "新增不能指定ID", groups = {AddGroup.class})
	@TableId
	private Long brandId;

    @NotBlank(message = "名称不能为空", groups = {AddGroup.class, UpdateGroup.class})
	private String name;

    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母")
    private String firstLetter;

NotNull和Null是校验类下规则,message指定校验失败时的报错信息。groups是指定分组(UpdateGroup和AddGroup是自定义的接口类),需要配合参数注解指定分组来确定是否需要校验。

2.开启校验功能,可以使用java规范(javax.validation.Valid)@Valid,但是该注解没有任何属性。我们可以使用Spring自带的(org.springframework.validation.annotation.Validated)@Validated可以支持分组功能。

@RequestMapping("/update")
public RespData update(@Validated({UpdateGroup.class}) @RequestBody BrandEntity brand)

UpdateGroup.class指定这是更新操作,只有上面1中属于UpdateGroup分组的字段才需要校验,

@RequestBody 自动将传递的json参数映射为BrandEntity实体对象。

注意:默认没有指定分组的校验注解,在分组校验情况下不生效,只会在@Validated生效;

3.获取校验结果:

        方式一通过参数后紧跟BindingResult对象:        

public RespData save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand, BindingResult result){
	if(result.hasErrors()){
	    Map<String, String> map = new HashMap<>();
	    //获取校验的错误结果
	    result.getFieldErrors().forEach((item) -> {
	        //FieldError获取错误提示
	        String message = item.getDefaultMessage();
	        //获取错误的属性的名字
	        String field = item.getField();
	        map.put(field, message);
	    });
	    return RespData.error(400, "提交的数据不合法").put("data", map);
	}
}

方式二统一处理异常,自定义异常处理类:

/**
 * 集中处理所有异常
 */
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.example.controller")
@RestControllerAdvice(basePackages = "com.example.controller")
public class ExampleExceptionControllerAdvice {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public RespData handleValidException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String, String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError) -> {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        });
        return RespData.error(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public RespData handleException(Throwable throwable){
        return RespData.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

错误类型枚举类:

public enum BizCodeEnume {

    UNKNOW_EXCEPTION(10000, "系统未知异常"),
    VALID_EXCEPTION(10001, "参数格式校验失败");

    private int code;
    private String msg;

    BizCodeEnume(int code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

@RestControllerAdvice是@ResponseBody和@ControllerAdvice的合并效果,

basePackages指定处理该包下的异常。

@ExceptionHandler指定处理的异常

以上即可实现基本的数据校验功能,但有时候需要我们自定义校验方法,创建自定义校验器

例如我们要实现一个字段只能是指定值中的一个:

@ListValue(vals= {0, 1}, message = "必须是指定的值")
private Integer status;

首先需要导入依赖:

<dependency>
   <groupId>jakarta.validation</groupId>
   <artifactId>jakarta.validation-api</artifactId>
   <version>2.0.2</version>
   <scope>compile</scope>
</dependency>

然后我们可以参考@NotBlank编写:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {

    String message() default "{com.example.valid.ListValue.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int[] vals() default {};
}

message对应错误消息,可以在resources文件夹下创建ValidationMessages.properties文件,在文件中编写对应的错误消息:

com.example.valid.ListValue.message=必须是指定的值

也可以在写注解时指定message。

@Constraint(validatedBy = {ListValueConstraintValidator.class})确定注解的校验器:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private Set<Integer> set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals = constraintAnnotation.vals();
        for (int val : vals){
            set.add(val);
        }
    }

    /**
     *
     * @param value 需要校验的值
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(value);
    }
}

至此完成了ListValue的自定义校验功能。

### Spring Boot 中的统一结果集封装与全局异常处理 #### 结果集封装类设计 为了使API响应更加一致,在Spring Boot应用中通常会创建一个通用的结果集封装类`ResponseResult`来标准化返回给客户端的数据结构。 ```java public class ResponseResult<T> { private Integer code; // 响应状态码 private String message; // 提示信息 private T data; // 返回数据体 public static <T> ResponseResult<T> success(T data){ return new ResponseResult<>(HttpStatus.OK.value(), "操作成功", data); } public static ResponseResult<?> failure(Integer errorCode, String errorMessage){ return new ResponseResult<>(errorCode, errorMessage, null); } protected ResponseResult() {} protected ResponseResult(Integer code, String msg, T body) { this.code = code; this.message = msg; this.data = body; } // Getters and Setters... } ``` 此部分实现了对HTTP请求的成功和失败两种情况下的基本反馈机制[^1]。 #### JSR303 参数校验配置 对于表单提交或接口调用中的参数合法性验证,可以利用Hibernate Validator框架提供的JSR-303 Bean Validation标准来进行约束声明。当发生违反这些规则的情况时,系统能够自动触发相应的错误提示并阻止程序继续执行下去。 在实体对象上添加注解: ```java import javax.validation.constraints.NotBlank; public class UserDTO { @NotBlank(message="用户名不能为空") private String username; // Other fields... // Getter & Setter methods... } ``` 上述代码片段展示了如何通过简单的注释方式定义字段级别的检验条件。 #### 全局异常处理器设置 为了让应用程序具备良好的健壮性和用户体验,有必要建立一套完善的异常捕获逻辑。这可以通过继承`@ControllerAdvice`注解的方式实现跨多个控制器共享相同的异常处理策略;同时借助于`@ExceptionHandler`指定具体要拦截哪些类型的Exception实例。 ```java @ControllerAdvice public class GlobalExceptionHandler { /** * 处理MethodArgumentNotValidException异常(即JSR303校验不通过) */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ResponseResult<?>> handleValidationExceptions( MethodArgumentNotValidException ex) { List<String> errors = ex.getBindingResult() .getFieldErrors() .stream() .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.toList()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ResponseResult.failure(400,"参数校验失败:" + String.join(", ",errors))); } /** * 捕捉其他未预见运行期异常 */ @ExceptionHandler(Exception.class) public ResponseEntity<ResponseResult<?>> handleAllUncaughtException(Exception e) { logger.error("Unexpected error occurred.",e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ResponseResult.failure(500,e.getMessage())); } } ``` 这段Java源文件描述了一个用于集中管理各类业务内外部抛出的不同种类异常事件的方法集合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值