校验参数的6大神功!
新手司机翻车实录"哥,注册接口又被刷爆了!
"某一个周末下午,我接到电话,打开日志一看,NullPointerException堆栈里有38个不同位置的校验逻辑。
原来新人小王在Controller里写满了这样的代码:
// 典型错误示范(转载自某小厂祖传代码)
public String register(UserDTO user) {
if (user.getName() == null) {
return "名字不能为空";
}
if (user.getAge() == null) {
return "年龄不能为空";
}
if (user.getAge() < 18) {
return "年龄不能小于18岁";
}
if (!user.getPhone().matches("^1\\d{9}$")) {
return "手机号不合法";
}
// ...后续还有20个if...
}这才是代码界的"九转大肠"——每个入口都让人窒息。
作为一位有很多开发经验的老司机,今天,老夫带你修炼参数校验的6大神功。
希望对你会有所帮助。
第一重:JSR规范基础功
1.1 HibernateValidator瞬炼大法
可以使用Hibernate中Validator框架做参数校验,具体代码如下:
public class UserDTO {
@NotBlank(message = "名称要填,皮这一下很开心?")
private String name;
@NotNull
@Min(value = 18, message = "未成年禁止入内")
@Max(60)
private Integer age;
@Pattern(regexp = "^1\\d{9}$", message = "这手机号是哪国来的?")
private String phone;
}
// Controller层启用校验(新手必知第一步)
@PostMapping("/register")
public Result register(@Valid @RequestBody UserDTO user) {
// 业务代码...
}技术要点:
[*]引入spring-boot-starter-validation依赖(调料包记得加)
[*]@Valid注解要放在入参侧(别贴在DTO类上)
[*]错误信息会进BindingResult(打扫战场需要手动处理)
第二重:全局异常擒龙手
2.1 统一异常拦截器
我们需要对异常进行统一拦截。
这样在出现参数校验异常,比如空指针时,不会把服务的内部错误信息直接输出给用户。
通过@RestControllerAdvice和@ExceptionHandler注解实现统一异常拦截器的功能。
具体代码如下:
@RestControllerAdvice
public class GlobalExceptionHandler {
// 专治各种不服校验
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
return Result.fail(result.getFieldError().getDefaultMessage());
}
}
// 返回格式规范(示例)
public class Result<T> {
private Integer code;
private String msg;
private T data;
public static <T> Result<T> fail(String message) {
return new Result<>(500, message, null);
}
}反爬虫机制:
[*]禁止直接暴露字段名给前端(攻击者会利用字段名信息)
[*]错误信息字典化管理(后面会教国际化这招)
第三重:自定义校验屠龙技
3.1 手机/邮箱二元校验
有时候,Hibernate Validator框架或者其他校验框架定义的校验不满足需求,我们需要自定义校验规则。
则可以自定义注解,实现ConstraintValidator接口,来实现具体的自定义的校验逻辑。
自定义注解@Contact在字段上使用。
具体代码如下:
@Target({FIELD, PARAMETER})@Retention(RUNTIME)@Constraint(validatedBy = ContactValidator.class)public @interface Contact { String message() default "联系方式格式错误"; Class[] groups() default {}; Class
页:
[1]