全局异常处理
全局异常的作用:可以捕捉系统内部产生的错误,不管是参数错误,还是程序错误,还是突然代码发神经导致的错误。但是这些错误我们都可以拿到,并且输出对应我们想要前台看到的信息。让前台更优雅的看到这些错误,不至于看到都是系统提示的英文鸟语。
全局异常扫描注解
在application启动类中,需要配置全局异常扫描:@EnableAutoConfiguration
//启动类09 @SpringBootApplication //扫描所有包 @MapperScan(basePackages = "com.wt.mapper") @ComponentScan(basePackages = {"com.wt"}) @EnableAutoConfiguration public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
包的结构
处理代码
1、返回的处理结果类型JsonData,Commonreturntype类
package com.wt.response; //通用返回格式 public class CommonReturnType { //摆明对应的请求的返回处理结果 success或fail private String status; //status= success 则data返回json数据 //status= fail 返回的错误码格式 private Object data; public static CommonReturnType create(Object result, String status) { CommonReturnType type = new CommonReturnType(); type.setStatus(status); type.setData(result); return type; } //定义一个通用的创建方法 public static CommonReturnType create(Object result) { return CommonReturnType.create(result, "success"); } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
2、错误接口类的定义:CommonError
package com.wt.error; public interface CommonError { //获取错误信息码 public int getErrCode(); //获取错误信息 public String getErrMsg(); //获取共同的错误 public CommonError setErrMsg(String errMsg); }
3、EmBusinessError定义枚举错误信息,这里存储了我们自己觉得需要抛出的异常文字定义,总之我们想要抛出啥错误信息,就需要在这里定义更多的枚举变量
package com.wt.error; import javax.xml.stream.events.Comment; public enum EmBusinessError implements CommonError { //枚举变量要放在最开始 //通用错误类型 10001 PARAMETER_VALIDATION_ERROR(10001, "参数不合法"), UNKNOWN_ERROR(10002, "未知错误"); EmBusinessError(int errCode, String errMsg) { this.errCode = errCode; this.errMsg = errMsg; } //成员变量可以靠后放置 private int errCode; private String errMsg; @Override public int getErrCode() { return this.errCode; } @Override public String getErrMsg() { return this.errMsg; } @Override public CommonError setErrMsg(String errMsg) { this.errMsg = errMsg; return this; } public void setErrCode(int errCode) { this.errCode = errCode; } }
4、BusinessException包装类业务异常类实现,这个又是另一种错误抛出的信息提示
package com.wt.error; //业务错误处理 public class BusinessException extends RuntimeException implements CommonError{ private CommonError commonError; //直接接收EmbusinessError 的传参用于构造业务异常 public BusinessException(CommonError commonError){ super(); this.commonError = commonError; } //接收自定义的errMsg的方式构造业务异常 public BusinessException(CommonError commonError, String errMsg) { super(); this.commonError= commonError; this.commonError.setErrMsg(errMsg); } @Override public int getErrCode() { return this.commonError.getErrCode(); } @Override public String getErrMsg() { return this.commonError.getErrMsg(); } @Override public CommonError setErrMsg(String errMsg) { this.commonError.setErrMsg(errMsg); return this; } public CommonError getCommonError() { return commonError; } }
5、GlobalExceptionHandler最后定义全局异常捕获类,这就相当于一个接口,一个监视器一样,我们程序出现错误就会有这样一个监管的人来逮住你
package com.wt.errorhandler; import com.wt.error.BusinessException; import com.wt.error.EmBusinessError; import com.wt.response.CommonReturnType; import org.slf4j.LoggerFactory; import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; //全局异常处理 @ControllerAdvice public class GlobalExceptionHandler { //日志监测 public static final org.slf4j.Logger logger=LoggerFactory.getLogger(GlobalExceptionHandler.class); //错误处理 @ExceptionHandler(Exception.class) @ResponseBody public CommonReturnType doError(HttpServletRequest httpServletReques, HttpServletResponse httpServletResponse, Exception ex) { ex.printStackTrace(); //map存储异常响应的数据 Map<String, Object> responseData = new HashMap<>(); //如果是业务异常类型 if (ex instanceof BusinessException) { System.out.println("===这里错误了进来了么哟==="); BusinessException businessException = (BusinessException) ex; //map中就放置 错误码和信息 responseData.put("errCode", businessException.getErrCode()); responseData.put("errMsg", businessException.getErrMsg()); // } else if (ex instanceof ServletRequestBindingException) { responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode()); //拦截到对应错误信息 输出对应的异常信息 responseData.put("errMsg", "url绑定路由问题"); } else if (ex instanceof NoHandlerFoundException) { responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode()); responseData.put("errMsg", "没有找到对应的访问路径"); } else { /*这里错误了没*/ responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode()); responseData.put("errMsg", EmBusinessError.UNKNOWN_ERROR.getErrMsg()); } //错误日志输出 logger.error(responseData.toString()); System.out.println("================这个是错误集合"+responseData.toString()); //回写错误集合 return CommonReturnType.create(responseData,"fail"); } }
测试
测试还是老规矩:在controller中进行测试,controller在api模块中,别搞忘了
//异常错误 @GetMapping("/exceptiontest") public CommonReturnType exceptionTest() { //这里传递一个参数 有个为空 代表参数错误 int i=1/0; return CommonReturnType.create(null, "fail"); }
访问路径:http://localhost:8088/exceptiontest
全局异常检测下的验证器
项目结构图
引入依赖
<!--校验相关 --> <!--apache工具包,方便常规操作 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <!--hibernate校验工具 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.4.Final</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> </dependency> <!--hutool工具 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.7</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-parent</artifactId> <version>5.3.7</version> <type>pom</type> </dependency>
校验器
1、校验结果的判断ValidationResult
package com.wt.validator; import org.apache.commons.lang3.StringUtils; import java.util.HashMap; import java.util.Map; //校验结果 //检验结果的正确与否 public class ValidationResult { //检验结果是否有错 private boolean hasErrors = false ; //存放错误信息 的map private Map<String, String> errorMsgMap = new HashMap<>(); public boolean isHasErrors() { return hasErrors; } public void setHasErrors(boolean hasErrors) { this.hasErrors = hasErrors; } public Map<String, String> getErrorMsgMap() { return errorMsgMap; } public void setErrorMsgMap(Map<String, String> errorMsgMap) { this.errorMsgMap = errorMsgMap; } //实现通用的通过格式化字符串信息获取错误结果的msg方法 public String getErrMsg() { return StringUtils.join(errorMsgMap.values().toArray(), ","); } }
2、校验器的实现类ValidatorImpl,这里会和全局异常配合使用,当校验的结果和规定的不符合,就会捕捉到错误的信息进行处理,并且返回规定的错误提示信息。vo类是页面向后台传递数据的对象,将页面数据封装与后台进行查询交互,这样一个对象可以很好的规范用户在页面的操作。免得搞一些小动作。
package com.wt.validator; import cn.hutool.core.map.MapUtil; import com.wt.error.BusinessException; import com.wt.error.EmBusinessError; import org.apache.ibatis.io.ResolverUtil; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import java.util.Set; @Component public class ValidatorImpl implements InitializingBean { private Validator validator; public void check(Object bean) throws BusinessException{ ValidationResult result = validate(bean); if(MapUtil.isNotEmpty(result.getErrorMsgMap())) { if(result.isHasErrors()) { throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, result.getErrorMsgMap().toString()); } } } //实现校验方法并返回校验结果 private ValidationResult validate(Object bean) { //验证器结果 final ValidationResult result = new ValidationResult(); //将验证器的结果放入集合 Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean); //如果有错误 if(constraintViolationSet.size()>0){ //有错误 result.setHasErrors(true); //打印错误集合 ,同时得到错误信息,和每个错误的属性名字 也就是错误code constraintViolationSet.forEach(ConstraintViolation->{ String errMsg = ConstraintViolation.getMessage(); String propertyName = ConstraintViolation.getPropertyPath().toString(); result.getErrorMsgMap().put(propertyName, errMsg); }); } return result; } @Override public void afterPropertiesSet() throws Exception { //将hibernate validator 通过工厂的初始化方式使其实例化 this.validator = Validation.buildDefaultValidatorFactory().getValidator(); } }
3、创建Vo对象
package com.wt.controller.viewobject; import lombok.*; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; //这个对象的用处还是蛮大的 一般用于界面封装界面提交的信息 //你想想 每次页面提交 信息的个数和格式 估摸着都和数据库的不一样 //那么我们在输入信息的时候就将信息封装成一个对象 那这个对象到后台去校验 操作 @ToString @NoArgsConstructor @AllArgsConstructor @Setter @Getter public class TestVo { @NotNull private Integer id; //这里给他规定字符的格式 @NotBlank @Length(min=1, max=3, message = "名称长度1-3") private String name; private Integer age; }
测试
老规矩还是在controller中进行添加测试模块,如果有不懂的需要看我前面的搭建。我是不会告诉你直接答案的。
@Resource private ValidatorImpl validator; @GetMapping("/validatorTest") public CommonReturnType testException(TestVo testVo) { validator.check(testVo); return CommonReturnType.create("success"); }
这里由于是封装了一个对象,参数是一个参数,所以用那个swagger2并不是很友好,不知道怎么去封装一个对象那就别玩了,还是老老实实去用一下postman,提交id,name。
输入和他规定不符合的,那么就会出现他提示我的错出来