本文分为两种方法,第一种适合于对于小程序这种简单的应用,只需要得到错误消息即可,第二章针对大型规范化的网页项目。
一、简易异常类封装:
1、创建自定义异常类
1、继承RuntimeException。
2、实现get和set方法(lombok工具类实现)
3、创建两个变量,一个用于保存状态码另一个保存错误消息。
4、创建可能使用到的构造方法
super(msg)表示将错误信息以我们平时看见的红色字样打印在控制台
import lombok.Data;
@Data//异常类
public class EmosException extends RuntimeException{
private int code=500;
private String msg;
//对该异常类的构造方法进行补充,不写的化会默认只有一个无参构造
public EmosException(String msg) {
super(msg);
this.msg = msg;
}
public EmosException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public EmosException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public EmosException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}
2、创建异常配置类
1、给这个类+@RestControllerAdvice注解表示是异常处理的配置类。
2、给该类的方法上+@ResponseBody注解表示返回的是JSON类型,@ExceptionHandler表示处理的异常是什么类型的异常。
3、书写异常处理方法,参数就是总异常类Exception。
4、用if else if等进行分支,用instanceof判断是什么类型的。
返回的类型都是字符串类型,保存在requset.data中,前台获取只需要request.data即可得到异常消息。
package com.example.emos.config;
import com.example.emos.exception.EmosException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j//打印日志信息用
@RestControllerAdvice//让系统知道这是处理异常的类
//步骤:1、将异常转化为对应的类型 2、调用里面的获取消息模块
public class ExceptionAdvice {
@ResponseBody//将异常的返回格式为json格式
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)//处理哪种异常
public String exceptionHandler(Exception e){
log.error("执行异常",e);//将异常打印在控制台
//处理后端验证失败
if(e instanceof MethodArgumentNotValidException){
//将异常强转为验证异常
MethodArgumentNotValidException exception= (MethodArgumentNotValidException) e;
//获取异常消息
return exception.getBindingResult().getFieldError().getDefaultMessage();
}
//自定义异常
else if(e instanceof EmosException){
EmosException exception= (EmosException) e;
return exception.getMsg();
}
//未授权类型
else if(e instanceof UnauthorizedException){
return "你不具备相关权限";
}
else{
return "后端执行异常";
}
}
}
二、复杂类型封装:
该封装方法将每一种异常对应的状态码,以及异常返回消息的统一封装
1、新建一个properties文件,用于保存所有的异常消息文本:
lin.codes[0] = ok
lin.codes[999] = 服务器未知异常
lin.codes[10000] = 通用错误
lin.codes[10001] = 通用参数错误
lin.codes[10002] = 资源未找到
lin.codes[10003] = 没有找到合适的登陆处理方法
lin.codes[10004] = 令牌不合法或者过期
lin.codes[10005] = 用户未被授权
lin.codes[10006] = 登陆失败
lin.codes[20000] = 用户类通用错误
lin.codes[20001] = 用户已存在
lin.codes[20002] = 用户不存在
lin.codes[20003] = 用户密码错误
lin.codes[20004] = 获取用户wx openid失败
2、写一个配置文件,将上面写好的各种异常消息在SpringBoot开启时就读取进去。
package com.lin.missyou.core.CodeConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
//指明共有的前缀
@ConfigurationProperties(prefix = "lin")
//指明异常文件的地址
@PropertySource(value = "classpath:config/exception-code.properties",encoding = "UTF-8")
//加入到SpringBoot容器
@Component
//将那个文件的所有内容都读到这个map中
public class ExceptionCodeConfiguration {
private Map<Integer, String> codes = new HashMap<>();
public Map<Integer, String> getCodes() {
return codes;
}
public void setCodes(Map<Integer, String> codes) {
this.codes = codes;
}
public String getMessage(int code){
String message = codes.get(code);
return message;
}
}
配置文件的位置如下:
3、此时我们新建一个父类继承RuntimeException,用于让所有的关于http的异常都继承它:
package com.lin.missyou.exception.http;
/**
* 这是下面所有小错误类的父类。
* 1、同包的所有类都继承这个类,不同异常的code和状态码不同。code码指的是前面写的异常信息的数组索引
* 2、根据状态码对异常进行分类。
* 3、例如商品没找到,或用户没找到,都抛出NotFoundException,状态码来表示是什么没找到
* */
public class HttpException extends RuntimeException{
protected Integer code;
protected Integer httpStatusCode = 500;
public Integer getCode() {
return code;
}
public Integer getHttpStatusCode() {
return httpStatusCode;
}
}
下面我们以一个NotFoundException为例:
- 继承HttpException。
- 编写一个有参构造
package com.lin.missyou.exception.http;
public class NotFoundException extends HttpException{
public NotFoundException(int code){
this.httpStatusCode = 404;
this.code = code;
}
}
我还创建了一个和它写法一致的异常类,只是httpStatusCode不同:
4、此时我们来到最核心的部分,全局异常管理类:
在这里将处理我们前面所写的全部异常,每当新增一个异常类就要在这里写好对应的处理方法。
1、打上@ControllerAdvice注解表示是处理异常类的
2、打上@ResponseBody 防止返回JSON对象出现问题
3、将刚刚写好的处理异常消息的封装类注入今来
4、@ExceptionHandler表示处理什么异常
5、@ResponseStatus表示该异常的http状态码是多少。该部分有两种处理方式:
- 通过@ResponseStatus注解直接指明对应的状态码
- 对于我们自己写的自定义异常类,由于以及指明了对应的httpStatus所以要通过ResponseEntity来返回。
完整代码:
//当有异常时会进入这个类
@ControllerAdvice //内部包含了Component注解,所以会被SpringBoot发现
@ResponseBody //因为异常返回的也是对象,所以要加这个注解
public class GlobalExceptionAdvice {
@Autowired //将异常配置文件与这个类相对应
private ExceptionCodeConfiguration codeConfiguration;
//一、处理一个已知hpptStatus的异常
@ExceptionHandler(value=Exception.class)
/**
* 设置Http的状态码,不设置的话都是200正常。设置方式有两种:
* 1、 @ResponseStatus来设置状态码。不灵活,不能动态设置
* 2、通过ResponseEntity来设置返回的属性
* */
@ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR)
public UnifyResponse handleException(HttpServletRequest req, Exception e) {
String requestUrl = req.getRequestURI(); //获取出现异常的地址
String method = req.getMethod(); //获取是GET还是POST方法
System.err.println(e);
return new UnifyResponse(9999, "服务器异常", method + " "+ requestUrl);
}
//二、处理自己写的那些httpStatus动态变化HttpException<-自己写的那个父类
@ExceptionHandler(HttpException.class)
/**2、通过ResponseEntity来设置返回的属性(状态码等)*/
public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest req, HttpException e){
//1、获取URL和请求方法
String requestUrl = req.getRequestURI();
String method = req.getMethod();
//2、封装返回对象
UnifyResponse message =
new UnifyResponse(
e.getCode() //获取自定义的异常状态码
,codeConfiguration.getMessage(e.getCode()) //通过这个自定义状态码来查询对应的消息
, method + " " + requestUrl); //封装方法和URL
//3、因为没用指定状态码,所以需要用ResponseEntity对象。
//3.1与ResponseBody注解用处相同,不加无法返回对象以及对中文等都无法解析
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//3.2通过异常来获取http状态码信息
HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
//4、返回对象
return new ResponseEntity<>(message, headers, httpStatus);
}
}
调用处:
@Override
public BannerByNameVo findByName(String name) {
BannerByNameVo bannerItems = bannerDao.selectByNames(name);
if (bannerItems==null)
throw new NotFoundException(10005);
return bannerItems;
}
返回形式如下: