参数校验规则
需要进行参数校验
- 对外提供的开放接口. 无论是RPC,API还是HTTP接口
- 敏感权限入口
- 需要极高稳定性和可用性的方法
- 调用频次低的方法
- 执行开销很大的方法:
- 参数校验的时间可以忽略不计
- 如果因为参数错误会导致中间执行被退回或者错误时代价很大
不需要进行参数校验
- 可能被循环调用的方法不需要进行参数校验,但是需要在方法说明中注明外部参数的检查要求
- 声明为private的方法,只会被本身类的代码调用:
- 如果已经确定调用的方法代码传入参数已经做过检查或者肯定不会有问题,则不需要进行参数校验
- 底层调用频度比较高的方法
JSR 303
基本概念
- JSR: Java Specification Requests.Java规范提案,指的是向JCP,也就是Java Community Process提出新增一个标准化技术规范的正式请求.任何人都可以提交JSR,来向Java平台新增API和服务
- JSR 303:
- JavaEE 6中的一项子规范,叫作Bean Validation
- Hibernate Validatior是Bean Validation的参考实现
- Hibernate Validator提供JSR 303规范中所有内置约束constraint的实现,而且还有一些附加的约束constraint
- SpringBoot中内置了Hibernate Validator
- JSR 303的作用:
- 用于对Java Bean的字段值进行校验,确保输入的数据在语义上的正确性,使得验证逻辑和业务代码相分离
- JSR 303是运行时数据验证框架,验证后的错误信息会立即返回
基本使用
@Data
public class User {
/**
* 用户名
*/
private String name;
}
@PostMapping("/user")
public Result addUser(@RequestBody User user) {
// 参数校验
if (StringUtils.isEmpty(user.getName())) {
throw new RuntimeException("用户姓名必须提交!");
}
return Result.buildSuccess(user);
}
- 使用if判断可以完成校验,但是如果遇到有很多属性需要校验的场景时,程序中就必须要有很多if判断,这样就会造成代码冗余.此时可以使用JSR 303完成校验
@NotBlank
统一异常处理
- 从输出结果上可以看到 @NotBlank中的message信息,对于错误的输出结果 ,Spring中提供了对应的类BindingResult来存储错误信息,为方法添加BindingResult参数:
@PostMapping("/user")
public Result addUser(@Valid @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(item -> {
// 获取错误提示信息message
String message = item.getDefaultMessage();
// 获取发生错误的字段
String field = item.getField();
map.put(field, message);
System.out.println(field + ":" + message);
});
return Result.buildFailure(400, "参数错误", map);
}
return Result.buildSuccess(user);
}
- Spring提供的MethodArgumentNotValidException类中,包含BindingResult的引用,因此BindingResult类可以获取到输出的错误信息
public class MethodArgumentNotValidException extends Exception {
private final MethodParameter parameter;
private final BindingResult bindingResult;
public MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult) {
this.parameter = parameter;
this.bindingResult = bindingResult;
}
public MethodParameter getParameter() {
return this.parameter;
}
public BindingResult getBindingResult() {
return this.bindingResult;
}
}
@Slf4j
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handValidationException(MethodArgumentNotValidException e) {
log.error(ErrorStatus.ILLEGAL_DATA.getMessage() + ":" + e.getMessage());
Map<String, Object> map = new HashMap<>();
// 获取异常输出信息
BindingResult bindingResult = e.getBindingResult();
bindingResult.getFieldErrors().forEach(fieldError -> {
String message = fieldError.getDefaultMessage();
String field = fieldError.getField();
map.put(field, message);
});
return Result.buildFailure(ErrorStatus.ILLEGAL_DATA.getCode(), String.valueOf(getFirstOrNull(map)), map);
}
private static Object getFirstOrNull(Map<String, Object> map) {
Object obj = null;
for (Map.Entry<String, Object> entry : map.entrySet()) {
obj = entry.getValue();
if (null != obj) {
break;
}
}
return obj;
}
}
JSR 303注解
