自定义异常
使用场景
前后端分离的项目,后端在处理过程需要给前端返回数据结果,但我们发现,在正常情况下只能返回200状态码,在异常情况下只能返回500状态码。显然这不满足我们的需求!
正常情况数据返回
spring提供的了ResponseEntity<T>
实体类来给前端返回相应的状态码
代码如下:
@PostMapping("/save")
public ResponseEntity<Long> saveItem(@RequestParam("id") Long id){
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(itemService.saveItem(id));
}
HttpStatus 枚举类
status():设置状态码
body(): 设置响应内容
常用正确情况状态码:
HttpStatus.OK: 200,用于查询成功
HttpStatus.CREATED: 201,用于添加成功
HttpStatus.NO_CONTENT:204,用于删除或修改成功
异常情况下数据返回
分析:
因为这里所有异常的状态码都是500,我们希望错误的状态码更细致一些,换句话来说,就是我们希望能够自己指定异常时的状态码。而RuntimeException异常又无法自定义状态码,只有自己定义一个异常类了。
一、自定义异常
- 继承 RuntimeException 设置响应码和错误信息
/**
* 自定义异常类
*/
@Getter
public class MyException extends RuntimeException{
private Integer status;//状态码
public MyException(Integer status,String message){
super(message);
this.status = status;
}
}
设置测试 :
- 如果 id 是1 就抛出自定义异常
@Service
public class ItemService {
public Long saveItem(Long id){
// 模拟添加操作,如果id为1抛异常
if(id.equals(1L)){
throw new MyException(501, "Id不能为1!");
}
return id;
}
}
但是我们发现 : spring并不认识我们的异常类 ,依旧当RuntimeException异常进行处理
二、 自定义异常拦截器
我们需要一个异常拦截器 去告诉 spring 我们的异常类
@ControllerAdvice
声明这是一个异常拦截器
@ExceptionHandler(MyException.class)
声明需要捕获的异常类型
/**
* 全局异常处理类
*/
@ControllerAdvice
public class MyExceptionController {
/**
* 处理异常方法
*/
@ExceptionHandler(MyException.class) //声明需要捕获的异常类型
@ResponseBody // 把返回值转换为json格式
public ResponseEntity<MyException> handlerException(MyException e){
return ResponseEntity.status(e.getStatus()).body(e);
}
}
但是我们发现:
- 返回的异常信息有我们需要的信息,但是也有大量冗余的的信息
三、 异常结果集类
将我们需要的信息进行封装
/**
* 封装异常结果信息
*/
@Getter
public class ExceptionResult {
private Integer status;
private String message;
public ExceptionResult(LyException e){
this.status = e.getStatus();
this.message = e.getMessage();
}
}
四、 改造异常拦截器
将body返回的 替换成我们的异常结果集
/**
* 全局异常处理类
*/
@ControllerAdvice
public class MyExceptionController {
/**
* 异常处理方法
*/
@ExceptionHandler(value = MyException.class) // 捕获LyException异常
@ResponseBody // 转换为json格式
public ResponseEntity<ExceptionResult> resolveException(LyException e){
return ResponseEntity.status(e.getStatus()).body(new ExceptionResult(e));
}
}
五、 自定义枚举类 : 模仿ResponseEntity
@Getter
public enum ExceptionEnum {
INVALID_FILE_TYPE(400, "无效的文件类型!"),
INVALID_PARAM_ERROR(400, "无效的请求参数!"),
INVALID_PHONE_NUMBER(400, "无效的手机号码"),
INVALID_VERIFY_CODE(400, "验证码错误!"),
INVALID_USERNAME_PASSWORD(400, "无效的用户名和密码!"),
INVALID_SERVER_ID_SECRET(400, "无效的服务id和密钥!"),
INVALID_NOTIFY_PARAM(400, "回调参数有误!"),
INVALID_NOTIFY_SIGN(400, "回调签名有误!"),
private int status;
private String message;
ExceptionEnum(int status, String message) {
this.status = status;
this.message = message;
}
}
六、改造自定义异常类
/**
* 自定义异常类
*/
@Getter
public class MyException extends RuntimeException{
private Integer status;//状态码
public MyException(Integer status,String message){
super(message);
this.status = status;
}
public MyException(ExceptionEnum exceptionEnum){
super(exceptionEnum.getMessage());
this.status = exceptionEnum.getStatus();
}
}
七、最终的效果
throw new MyException(ExceptionEnum.INVALID_FILE_TYPE);
@Service
public class ItemService {
public Long saveItem(Long id){
// 模拟添加操作,如果id为1抛异常
if(id.equals(1L)){
throw new MyException(ExceptionEnum.INVALID_FILE_TYPE);
}
return id;
}
}