SpringBoot restful返回值+统一异常处理+参数校验
restful返回值
自定义错误类型
新建枚举类,写入错误码及错误提示
/**
* 接口返回错误码,错误提示
* 参考 https://blog.csdn.net/alisonyu/article/details/82833413
*/
public enum ResultConsts {
SYSERR(-1,"系统错误"),
OK(0,"请求成功"),
ILLGAL_PARAM(40001,"参数无效"),
;
private Integer code;
private String msg;
ResultConsts(Integer code, String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode(){
return code;
}
public void setCode(Integer code){
this.code = code;
}
public String getMsg(){
return msg;
}
public void setMsg(String msg){
this.msg = msg;
}
}
Restful接口返回值拦截
/**
* 捕获所有Rest接口返回值
* springboot-Rest接口返回统一格式数据
* 参考 https://blog.csdn.net/alisonyu/article/details/82833413
*/
@ControllerAdvice(annotations = RestController.class)
public class ResultHandler implements ResponseBodyAdvice{
private ThreadLocal<ObjectMapper> mapperThreadLocal = ThreadLocal.withInitial(ObjectMapper::new);
private static final Class[] annos={
RequestMapping.class,
GetMapping.class,
PostMapping.class,
DeleteMapping.class,
PutMapping.class
};
/**
* 对所有RestController的接口方法进行拦截
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType){
AnnotatedElement element = returnType.getAnnotatedElement();
return Arrays.stream(annos).anyMatch(anno -> anno.isAnnotation() && element.isAnnotationPresent(anno));
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Object out;
ObjectMapper mapper = mapperThreadLocal.get();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
/*根据返回数据类型,封装返回值*/
if (body instanceof ResultConsts){
return ResultUtils.getResult((ResultConsts) body);
}
else if(body == null){
return ResultUtils.getResult(ResultConsts.OK);
}
else{
return ResultUtils.getResult(ResultConsts.OK, body);
}
}
}
统一异常处理
自定义异常
/**
* 自定义异常,对应ResultConsts
*/
public class DefaultException extends RuntimeException{
//异常码
private Integer code;
//异常信息
private String msg;
public DefaultException(Integer code, String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode(){
return code;
}
public void setCode(Integer code){
this.code = code;
}
public String getMsg(){
return msg;
}
public void setMsg(String msg){
this.msg = msg;
}
}
统一异常捕获
使用@ControllerAdvice进行注解
/**
* 全局异常捕获
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* Exception,默认异常
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public Object ExceptionHandler(Exception ex){
return ResultUtils.getResult(ResultConsts.SYSERR);
}
/**
* DefaultException,自定义异常
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = DefaultException.class)
public Object ExceptionHandler(DefaultException ex){
return ResultUtils.getResult(ex.getCode(),ex.getMsg());
}
/**
* ValidationException,参数校验异常
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = ValidationException.class)
public Object validationExceptionHandler(ValidationException ex){
ConstraintViolationException ex1 = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> violations = ex1.getConstraintViolations();
String errorInfo = "";
for(ConstraintViolation<?> item : violations){
errorInfo = errorInfo + " " + item.getMessage();
}
return ResultUtils.getResult(ResultConsts.ILLGAL_PARAM.getCode(), ResultConsts.ILLGAL_PARAM.getMsg() + errorInfo);
}
}
示例
@RestController
@RequestMapping(value="/demo")
public class DemoController {
@GetMapping(value = "/err")
public String err(){
throw new DefaultException(0,"Error");
}
@GetMapping(value = "/error")
public Integer error(){
return 0/0;
}
}
参数校验
hibernate validator配置
/**
* Hibernate 参数校验
* 参考 https://www.jianshu.com/p/aa8b3163b30a
*/
@Configuration
@EnableAutoConfiguration
public class ValidatorConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(){
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
postProcessor.setValidator(validator());
return postProcessor;
}
/**
* 使用快速校验模式,一旦出错立刻返回
* @return
*/
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
.addProperty("hibernate.validator.fail_fast", "true")
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
使用hibernate validator注解
Bean Validation 中内置的 constraint
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
示例
使用@Validated进行注解
@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController {
@GetMapping(value = "/email")
public String email(@Email(message = "邮件格式错误") String email){
return "ok";
}
}
自定义校验
自定义校验注解
/**
* 自定义校验注解
* 参考 https://www.jianshu.com/p/320cee90391f
*/
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER,ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
//java标准校验注解,validateBy指定校验的类
@Constraint(validatedBy = PwdValidator.class)
@Documented
public @interface PwdCheck {
//校验注解中必须实现以下三个属性
String message() default ValidatorConsts.ILLGAL_PWD;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
校验实现
/**
* 实现ConstraintValidator接口,第一个泛型指定用于标注验证的注解,第二个泛型指定倍标注值得类型
* 不需要用@component等注解将验证类加入容器。spring会自动将此类加入容器
*/
public class PwdValidator implements ConstraintValidator<PwdCheck,String> {
//在这个类中可以使用Spring @Autowire注解注入任何需要的对象
private final static String regExp = "^(?=.*[a-zA-Z0-9].*)(?=.*[a-zA-Z\\W].*)(?=.*[0-9\\W].*).{8,20}$";
/**
* 校验器初始化
* @param pwdCheck
*/
@Override
public void initialize(PwdCheck pwdCheck) {
}
/**
* 校验方法
* @param str 待校验的值
* @param constraintValidatorContext
* @return 返回true代表校验成功,false代表校验失败
*/
@Override
public boolean isValid(String str, ConstraintValidatorContext constraintValidatorContext) {
if(StringUtils.isBlank(str)){
return false;
}
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}
}
校验异常捕获
参考统一异常捕获
/**
* 全局异常捕获
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* ValidationException,参数校验异常
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = ValidationException.class)
public Object validationExceptionHandler(ValidationException ex){
ConstraintViolationException ex1 = (ConstraintViolationException) ex;
Set<ConstraintViolation<?>> violations = ex1.getConstraintViolations();
String errorInfo = "";
for(ConstraintViolation<?> item : violations){
errorInfo = errorInfo + " " + item.getMessage();
}
return ResultUtils.getResult(ResultConsts.ILLGAL_PARAM.getCode(), ResultConsts.ILLGAL_PARAM.getMsg() + errorInfo);
}
}
参数校验
@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController {
@GetMapping(value = "/pwd")
public String pwd(@PwdCheck(message = "密码格式错误" ) String pwd) {
return "ok";
}
@PostMapping(value = "post")
public String post(@PwdCheck(message = "密码格式错误" ) String pwd){
return "ok";
}
}
实体类校验
参数定义
/**
* 实体类参数
*/
public class UserInfo {
@NotBlank(message = ValidatorConsts.NOTBLANK_ID)
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
示例
@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController {
@PostMapping(value = "/userinfo")
public String userInfo(@Valid UserInfo userInfo, BindingResult result){
return "ok";
}
}
代码示例
https://github.com/sleetdream/spring-cloud-demo
参考
https://www.cnblogs.com/mr-yang-localhost/p/7812038.html#_label3
https://www.cnblogs.com/gdwkong/p/8969963.html
https://blog.csdn.net/alisonyu/article/details/82833413
https://www.cnblogs.com/NeverCtrl-C/p/8185576.html
https://blog.csdn.net/hutaodsb931211/article/details/78777947
https://blog.csdn.net/u013815546/article/details/77248003
https://www.cnblogs.com/magicalSam/p/7198420.html
https://blog.csdn.net/qq_33996921/article/details/79568456
https://www.jianshu.com/p/aa8b3163b30a
https://blog.csdn.net/fred_lzy/article/details/81099699
https://blog.csdn.net/qq_40325734/article/details/81782955
https://www.jianshu.com/p/320cee90391f
https://blog.csdn.net/matengbing/article/details/83188833