SpringBoot参数校验
一、pom文件
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>2.0.2</version>
</dependency>
<!--SpringBoot 2.3中@Valid会失效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、注解含义
注解 | 含义 |
---|---|
@Null | 值为null |
@NotNull | 不能为null |
@Pattern | 必须满足给定的正则表达式 |
满足Email格式 | |
@Max | 必须是数字,且值小于等于给定的值 |
@Min | 必须是数字,且值小于等于给定的值 |
@Range | 元素需在指定范围区间内 |
三、代码示例
1 代码目录
2自定义参数规则
2.1 注解,这里是对orderByValue的值的内容进行校验,必须为desc 或者asc,其它字符串则报错
package com.example.shiro.annotation;
import com.example.shiro.validator.MyValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = MyValidator.class)
@Documented
public @interface MyValidate {
String message() default "orderByValue 的值不在可选范围内[desc,asc]";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.2 自定义validator,校验参数是否包含asc 或者 desc
package com.example.shiro.validator;
import com.example.shiro.annotation.MyValidate;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
public class MyValidator implements ConstraintValidator<MyValidate, String> {
/**
* 验证参数是不是在指定值中
* @param value
* @param context
* @return
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
HashSet<Object> typeName = new HashSet<>();
typeName.add("desc");
typeName.add("asc");
return typeName.contains(value);
}
}
3 自定义分组validator,创建lclass标识不通的分组
package com.example.shiro.validator;
public interface AreaCon {
}
package com.example.shiro.validator;
public interface TestCon {
}
4 实体类
1)校验参数值不可以为空
@NotNull(message = “num 值不允许为null”)
private Integer num;
2)分组参数校验,当控制层使用TestCon.class分组时 则校验page值不允许为空
@NotNull(groups = {TestCon.class},message = “当前页面不允许为空”)
private String page ; //当前页面
3)使用自定义注解参数校验,orderByValue必须是asc 或者 desc
@NotNull(message = “排序方式不允许为空”)
@MyValidate
private String orderByValue;
package com.example.shiro.entity;
import com.example.shiro.annotation.MyValidate;
import com.example.shiro.validator.AreaCon;
import com.example.shiro.validator.TestCon;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class PageList {
/**
* 排序方式 desc or asc
*/
@NotNull(message = "排序方式不允许为空")
@MyValidate
private String orderByValue;
/**
* 当前页
*/
@NotNull(groups = {TestCon.class},message = "当前页面不允许为空")
private String page ; //当前页面
/**
* 是否默认 0 否 1 是
*/
@NotNull(groups = {AreaCon.class},message = "flag不允许为空")
private String flag;
}
5 控制层代码
package com.example.shiro.controller;
import com.example.shiro.annotation.MyLogs;
import com.example.shiro.entity.MyResult;
import com.example.shiro.entity.PageList;
import com.example.shiro.validator.AreaCon;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.groups.Default;
@RestController
public class MyLogApi {
@GetMapping("/hello")
@MyLogs()
public String getResHomeInformation(){
return "hello api";
}
@PostMapping("/hello2")
@MyLogs()
public MyResult getResBuryListInfo(@Validated @RequestBody PageList pageList) {
return new MyResult(true,"hello api 2");
}
@PostMapping("/hello3")
@MyLogs()
public MyResult getResBuryListInfoa(@Validated({AreaCon.class}) @RequestBody PageList pageList) {
return new MyResult(true,"hello api 3");
}
@PostMapping("/hello4")
@MyLogs()
public MyResult getResBuryListInfoa4(@Validated({AreaCon.class,Default.class}) @RequestBody PageList pageList) {
return new MyResult(true,"hello api 4");
}
}
6 全局异常处理
6.1 全局异常捕获
package com.example.shiro.handler;
import com.example.shiro.entity.MyResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Slf4j
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public MyResult expHandler(HttpServletRequest request, Exception e) {
e.printStackTrace();
return new MyResult(e);
}
}
6.2 具体异常处理,code=1013为传递参数异常
package com.example.shiro.entity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.shiro.constants.MyConstants;
import com.example.shiro.enums.MyCode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
import java.io.Serializable;
import java.net.ConnectException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyResult<T> implements Serializable {
private static final long serialVersionUID = 1071681926787951549L;
private Boolean success;
/**
*<p>状态码</p>
*/
private Integer code;
/**
* <p>业务码</p>
*/
private String operate;
/**
*<p> 状态说明</p>
*/
private String message;
/**
* <p>返回数据</p>
*/
private T data;
public MyResult(boolean success) {
this.success = success;
this.code = success ? MyCode.SUCCESS.getCode() : MyCode.COMMON_FAIL.getCode();
this.message = success ? MyCode.SUCCESS.getMessage() : MyCode.COMMON_FAIL.getMessage();
}
public MyResult(boolean success, MyCode resultEnum) {
this.success = success;
this.code = success ? MyCode.SUCCESS.getCode() : (resultEnum == null ? MyCode.COMMON_FAIL.getCode() : resultEnum.getCode());
this.message = success ? MyCode.SUCCESS.getMessage() : (resultEnum == null ? MyCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
}
public MyResult(boolean success, String message) {
this.success = success;
this.code = success ? MyCode.SUCCESS.getCode() : MyCode.COMMON_FAIL.getCode();
this.message = message;
}
public MyResult(boolean success, T data) {
this.success = success;
this.code = success ? MyCode.SUCCESS.getCode() : MyCode.COMMON_FAIL.getCode();
this.message = success ? MyCode.SUCCESS.getMessage() : MyCode.COMMON_FAIL.getMessage();
this.data = data;
}
public MyResult(boolean success, MyCode resultEnum, T data) {
this.success = success;
this.code = success ? MyCode.SUCCESS.getCode() : (resultEnum == null ? MyCode.COMMON_FAIL.getCode() : resultEnum.getCode());
this.message = success ? MyCode.SUCCESS.getMessage() : (resultEnum == null ? MyCode.COMMON_FAIL.getMessage() : resultEnum.getMessage());
this.data = data;
}
public MyResult(Throwable throwable) {
this.operate = MyConstants.OPERATE_FAILED;
if (throwable instanceof NullPointerException) {
this.code = 1001;
this.message = "空指针:" + throwable;
} else if (throwable instanceof ClassCastException) {
this.code = 1002;
this.message = "类型强制转换异常:" + throwable;
} else if (throwable instanceof ConnectException) {
this.code = 1003;
this.message = "链接失败:" + throwable;
} else if (throwable instanceof IllegalArgumentException) {
this.code = 1004;
this.message = "传递非法参数异常:" + throwable;
} else if (throwable instanceof NumberFormatException) {
this.code = 1005;
this.message = "数字格式异常:" + throwable;
} else if (throwable instanceof IndexOutOfBoundsException) {
this.code = 1006;
this.message = "下标越界异常:" + throwable;
} else if (throwable instanceof SecurityException) {
this.code = 1007;
this.message = "安全异常:" + throwable;
} else if (throwable instanceof SQLException) {
this.code = 1008;
this.message = "数据库异常:" + throwable;
} else if (throwable instanceof ArithmeticException) {
this.code = 1009;
this.message = "算术运算异常:" + throwable;
} else if (throwable instanceof DuplicateKeyException) {
this.code = 1011;
this.message = "存在重复数据,标签名称: " + throwable.getMessage().substring(throwable.getMessage().indexOf("'") + 1, throwable.getMessage().indexOf("-"));
} else if (throwable instanceof MissingServletRequestParameterException) {
this.code = 1012;
this.message = "参数缺失," + throwable.getMessage().substring(throwable.getMessage().indexOf("'"));
} else if (throwable instanceof UnknownAccountException) {
this.code = MyCode.USER_ACCOUNT_NOT_EXIST.getCode();
this.message = MyCode.USER_ACCOUNT_NOT_EXIST.getMessage();
} else if (throwable instanceof IncorrectCredentialsException) {
this.code = MyCode.USER_CREDENTIALS_ERROR.getCode();
this.message = MyCode.USER_CREDENTIALS_ERROR.getMessage();
} else if (throwable instanceof AuthenticationException) {
this.code = MyCode.USER_ACCOUNT_EXPIRED.getCode();
this.message = MyCode.USER_ACCOUNT_EXPIRED.getMessage();
} else if (throwable instanceof RuntimeException) {
this.code = 1010;
this.message = "运行时异常:" + throwable;
} else if (throwable instanceof MethodArgumentNotValidException) {
this.code = 1013;
List<ObjectError> allErrors = ((MethodArgumentNotValidException) throwable).getBindingResult().getAllErrors();
List<Map> result = new ArrayList<>();
for(int i=0;i<allErrors.size();i++){
final String s = JSON.toJSONString(allErrors.get(i));
JSONObject jsonObject = JSONObject.parseObject(s);
String field = jsonObject.getString("field");
if(field==null){
field = "";
}
String defaultMessage = allErrors.get(i).getDefaultMessage();
Map<String,Object> map = new HashMap<>();
map.put(field,defaultMessage);
result.add(map);
}
this.message = "参数校验不通过";
this.data = (T)result;
} else if (throwable instanceof Exception) {
this.code = 9999;
this.message = "系统繁忙,请稍后重试:" + throwable.getMessage();
}
}
}
7 代码运行结果
接口 | 参数 | 结果 |
---|---|---|
http://127.0.0.1:8000/hello2 | { } | { “success”: null, “code”: 1013, “operate”: “failed”, “message”: “参数校验不通过”, “data”: [ { “orderByValue”: “排序方式不允许为空” }, { “num”: “num 值不允许为null” }, { “orderByValue”: “orderByValue 的值不在可选范围内[desc,asc]” } ]} |
http://127.0.0.1:8000/hello3 | { } | { “success”: null, “code”: 1013, “operate”: “failed”, “message”: “参数校验不通过”, “data”: [ { “flag”: “flag不允许为空” } ]} |
http://127.0.0.1:8000/hello4 | { } | { “success”: null, “code”: 1013, “operate”: “failed”, “message”: “参数校验不通过”, “data”: [ { “num”: “num 值不允许为null” }, { “orderByValue”: “orderByValue 的值不在可选范围内[desc,asc]” }, { “flag”: “flag不允许为空” }, { “orderByValue”: “排序方式不允许为空” } ]} |