上文讲了如何对返回的数据格式进行统一处理,本文主要是进一步对异常情况的补充。
在后台接口处理的过程中会产生各种各样的异常,比如参数校验异常、系统内部异常、业务逻辑异常等,这些异常产生在各个环节,不能随意抛给调用方,格式不一致会导致调用方无法处理。
实现思路:
1)对后台接口(Controller)中的异常进行处理,使用@ControllerAdvice+@ExceptionHandler的方式进行处理;
2)对于Filter等类产生的异常,上面的方式是无法处理到的,在spring boot中默认将异常统一转发到"/error",所以只需要处理"/error"即可。
- 定义统一异常类ServiceException.java
package com.kevin.core.exception;
/**
* 统一异常
* @author Kevin Yu
*/
public class ServiceException extends RuntimeException {
private Integer code = 400;
public Integer getCode() {
return code;
}
public ServiceException() {
}
public ServiceException(Throwable e) {
super(e);
}
public ServiceException(String message) {
super(message);
}
public ServiceException(Integer code, String message) {
super(message);
this.code = code;
}
public ServiceException(String message, Throwable e) {
super(message, e);
}
}
- 定义全局异常处理类
package com.kevin.core.exception;
import com.kevin.core.result.ApiResultEntity;
import com.kevin.core.servlet.ServletHolderFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 统一异常处理,仅适用于Controller,无法处理Filter中的异常
* @author Kevin Yu
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger=LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public ApiResultEntity handleException(Exception e){
logger.error(String.format("[%s][%s]请求出现异常,URL:%s,异常信息:%s", ServletHolderFilter.getClientIP(),ServletHolderFilter.getUserAgent(),ServletHolderFilter.getServletPath(), msg), e);
return new ApiResultEntity(ApiResultEntity.RESULT_EXCEPTION, e.getMessage());
}
@ExceptionHandler(ServiceException.class)
@ResponseBody
public ApiResultEntity handleException(ServiceException e){
logger.warn("[{}][{}]请求出现业务异常,URL:{},异常信息:{}", ServletHolderFilter.getClientIP(),ServletHolderFilter.getUserAgent(),ServletHolderFilter.getServletPath(), e.getMessage());
return new ApiResultEntity(e.getCode(), e.getMessage());
}
}
- 实现"/error"接口,对异常结果进行统一处理
package com.kevin.core.base;
import com.kevin.core.result.ApiResultEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* 自定义Error处理类
* Spring Boot默认会将异常转发到"/error",交给{@link DefaultErrorAttributes}处理
* @Author kevin
* @create 2020/5/26 10:28
*/
@RestController
public class BaseErrorController implements ErrorController {
private final Logger logger = LoggerFactory.getLogger(getClass());
private ErrorAttributes errorAttributes;
private static final String ERROR_PATH = "/error";
@Autowired
public BaseErrorController(ErrorAttributes errorAttributes) {
this.errorAttributes = errorAttributes;
}
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@RequestMapping(value = ERROR_PATH)
public Object errorJson(HttpServletRequest request) {
Map<String, Object> errorInfo = errorAttributes.getErrorAttributes(new ServletWebRequest(request), true);
logger.error("请求异常:{}", errorInfo.toString());
return new ApiResultEntity((Integer)errorInfo.get("status"), (String) errorInfo.get("message"));
}
}
4.测试
package com.kevin.fish.controller.demo;
import com.kevin.core.exception.ServiceException;
import com.kevin.core.result.ApiResult;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author kevin
* @create 2020/8/7 14:42
*/
@ApiResult
@RestController
public class TestController {
@GetMapping(value="/test",produces= MediaType.APPLICATION_JSON_VALUE)
public Object test(){
throw new ServiceException("I am sorry");
}
}
调用接口 http://localhost:8081 返回结果:
{“code”:500,“text”:“I am sorry”,“data”:null}