@PostMapping(value="/error1")
public void error1( ){
int i=9/0;
}
int i=9/0 如果不用try-catch捕获的话,客户端就会收到如下异常
{
"timestamp": "2019-09-08T02:54:27.415 0000",
"status": 500,
"error": "Internal Server Error",
"message": "/ by zero",
"path": "/user/error1"
}
返回这种格式,明显不友好,故,我们可以用全局异常处理器来统一处理。
@PostMapping(value="/error4")
public void error4( ){
throw new RuntimeException("用户已存在!!");
}
public enum ResultCode {
/* 成功状态码 */
SUCCESS(0, "成功"),
/* 系统500错误*/
SYSTEM_ERROR(10000, "系统异常,请稍后重试"),
/* 参数错误:10001-19999 */
PARAM_IS_INVALID(10001, "参数无效"),
/* 用户错误:20001-29999*/
USER_HAS_EXISTED(20001, "用户名已存在");
private Integer code;
private String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer code() {
return this.code;
}
public String message() {
return this.message;
}
}
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ErrorResult {
/**
* 异常状态码
*/
private Integer status;
/**
* 用户看得见的异常,例如 用户名重复!!,
*/
private String message;
/**
* 异常的名字
*/
private String exception;
/**
* 异常堆栈的精简信息
*/
private Object errors;
public static ErrorResult fail(ResultCode resultCode, Throwable e,String message) {
ErrorResult result = ErrorResult.fail(resultCode, e);
result.setMessage(message);
return result;
}
public static ErrorResult fail(ResultCode resultCode, Throwable e) {
ErrorResult result = new ErrorResult();
result.setMessage(resultCode.message());
result.setStatus(resultCode.code());
result.setException(e.getClass().getName());
result.setErrors(e.getStackTrace());
return result;
}
}
@RestController
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理运行时异常
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
//TODO 运行是异常,可以在这里记录,用于发异常邮件通知
ErrorResult error =ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
log.error("URL:{} ,系统异常: ",request.getRequestURI(), e);
return error;
}
}
handleThrowable方法的作用是:捕获运行时异常,并把异常统一封装为ErrorResult对象。以上有几个细节点我们要单独讲解:
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping(value="/error1")
public void error1( ){
int i=9/0;
}
}
在浏览器输入 http://127.0.0.1:9090/user/error1 得到以下结果
{
"status": 10000,
"message": "系统异常,请稍后重试",
"exception": "java.lang.ArithmeticException"
}
自定义异常通常集成RuntimeException,代码很简单不细讲。
@Data
public class BusinessException extends RuntimeException {
protected Integer code;
protected String message;
public BusinessException(ResultCode resultCode) {
this.code = resultCode.code();
this.message = resultCode.message();
}
}
全局异常处理器只要在上文的基础上,增加一个自定义异常处理方法即可,代码很简单,如下:
@RestController
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/** 处理自定义异常 */
@ExceptionHandler(BusinessException.class)
public ErrorResult handleBusinessException(BusinessException e, HttpServletRequest request) {
ErrorResult error = ErrorResult.builder().status(e.code)
.message(e.message)
.exception(e.getClass().getName())
.build();
log.warn("URL:{} ,业务异常:{}", request.getRequestURI(),error);
return error;
}
/**
* 处理运行时异常
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class)
public ErrorResult handleThrowable(Throwable e, HttpServletRequest request) {
//TODO 运行是异常,可以在这里记录,用于发异常邮件通知
ErrorResult error =ErrorResult.fail(ResultCode.SYSTEM_ERROR, e);
log.error("URL:{} ,系统异常: ",request.getRequestURI(), e);
return error;
}
}
在ResultCode枚举类中加一个枚举,USERHASEXISTED(20001, "用户名已存在")。
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping(value="/error3")
public void error3( ){
throw new BusinessException(ResultCode.USER_HAS_EXISTED);
}
}
在浏览器输入 http://127.0.0.1:9090/user/error3 得到以下结果
{
"status": 20001,
"message": "用户名已存在",
"exception": "com.agan.boot.exceptions.BusinessException"
}
我们要改造《接口返回值统一标准格式》的代码,因为我们在讲解《接口返回值统一标准格式》的时候,就没有处理异常。
@ControllerAdvice(basePackages = "com.agan.boot")
public class ResponseHandler implements ResponseBodyAdvice
以上代码是在《接口返回值统一标准格式》的基础上增加了
if (o instanceof ErrorResult) {
ErrorResult errorResult = (ErrorResult) o;
return Result.fail(errorResult.getStatus(),errorResult.getMessage());
}
其实封装很简单,就是把ErrorResult对象转换Result对象即可。
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@PostMapping(value="/error3")
public void error3( ){
throw new BusinessException(ResultCode.USER_HAS_EXISTED);
}
}
在浏览器输入 http://127.0.0.1:9090/user/error3 得到以下结果
{
"status": 20001,
"desc": "用户名已存在",
"data": null
}
自己手写一个《全局异常处理器》和《接口返回值统一标准格式》
1.模拟一个空指针异常,然后返回以下接口返回值统一标准格式:
{
"code": 10000,
"msg": "系统异常请稍后...",
"data": null
}
2.模拟用户登录,提示以下内容
{
"code": 10000,
"msg": "用户名或密码错误,请重试",
"data": null
}