场景
在Controller接口抛出了异常,但接口内没捕捉抛给框架了,框架统一对这类错误处理;也可能是spring boot调度接口出问题,没进入到接口异常,比如接口参数转换失败或服务器找不到。
解决方案
这里有三种
ZUUL拦截
参考:
Spring Cloud实战小贴士:Zuul统一异常处理(一)
Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】
这种只能处理网关能获取到的异常,对于别的微服务不能捕获到,再一个就是返回的格式不符合自己要的格式;
异常页面跳转
SpringBoot在页面
发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径
application.yaml
server:
port: 8080
error:
path: /custom/error
这里我们使用spring boot原生的,默认的错误处理
异常处理Controller类(VndErrorController)
@Controller
public class VndErrorController implements ErrorController {
private Logger logger = LoggerFactory.getLogger(VndErrorController.class);
@Value("${error.path:/error}")
private String errorPath;
@Autowired
private ErrorAttributes errorAttributes;
@Override
public String getErrorPath() {
return errorPath;
}
@RequestMapping(value = "${error.path:/error}", produces = "application/vnd.error+json")
public @ResponseBody
ResponseEntity error(HttpServletRequest request) {
Map<String,Object> errorAttributes = getErrorAttributes(request, false);
Integer errrStatus=(Integer)errorAttributes.get("status");
String path=(String)errorAttributes.get("path");
String messageFound=(String)errorAttributes.get("message");
String message="";
if(!StringUtils.isEmpty(path)){
message=String.format("Requested path %s with result %s",path,messageFound);
}
logger.error("请求失败:{},status={} ",message,errrStatus);
CommonReturn errorVM = new CommonReturn();
final int status = getErrorStatus(request);
switch (status) {
case 500: //HttpStatus.INTERNAL_SERVER_ERROR
errorVM.setRetCode(ErrorCode.SERVER_ERROR);
break;
case 408: //REQUEST_TIMEOUT
case 504: //GATEWAY_TIMEOUT
errorVM.setRetCode(ErrorCode.REQUEST_TIMEOUT);
break;
case 405: //METHOD_NOT_ALLOWED
case 401: //UNAUTHORIZED
case 403: //FORBIDDEN
errorVM.setRetCode(ErrorCode.HTTP_REQUEST_ILLEGALITY);
break;
case 404: //NOT_FOUND
errorVM.setRetCode(ErrorCode.REQUEST_NOT_FOUND);
break;
case 400: //BAD_REQUEST
errorVM.setRetCode(ErrorCode.HTTP_REQUEST_FAIL);
break;
default:
errorVM.setRetCode(ErrorCode.HANDLE_FAIL);
}
errorVM.setRetMsg(ErrorCode.getText(errorVM.getRetCode()));
ResponseEntity.BodyBuilder builder;
builder = ResponseEntity.status(status);
return builder.body(errorVM);
}
private int getErrorStatus(HttpServletRequest request) {
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
return statusCode != null ? statusCode : HttpStatus.INTERNAL_SERVER_ERROR.value();
}
private String getErrorMessage(HttpServletRequest request) {
final Throwable exc = (Throwable) request.getAttribute("javax.servlet.error.exception");
return exc != null ? exc.getMessage() : "Unexpected error occurred";
}
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
}
}
spring mvc异常统一处理(ControllerAdvice注解)
这种方式我只在网关使用了,其它微服务器也是可以使用的
@ControllerAdvice没有捕获的异常,ErrorController会帮你“捡起来”,像404这类错误@ControllerAdvice没法捕捉
@ControllerAdvice
public class ExceptionTranslator {
private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);
@ExceptionHandler(ConcurrencyFailureException.class)
@ResponseStatus(HttpStatus.CONFLICT)
@ResponseBody
public Object processConcurrencyError(ConcurrencyFailureException ex) {
log.warn("An ConcurrencyError occurred: {}", ex);
return ..; //返回自定义的格式
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Object processValidationError(MethodArgumentNotValidException ex) {
log.warn("An ValidationError error occurred: {}", ex);
return ..; //返回自定义的格式
}
@ExceptionHandler(CustomParameterizedException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Object processParameterizedValidationError(CustomParameterizedException ex) {
log.warn("An ParameterizedValidationError occurred: {}", ex);
return ..; //返回自定义的格式
}
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public Object processAccessDeniedException(AccessDeniedException e) {
log.debug("An AccessDeniedException error occurred: {}", e);
return ..; //返回自定义的格式
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public Object processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
log.warn("An MethodNotSupportedException error occurred: {}", exception);
return ..; //返回自定义的格式
}
@ResponseBody
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> processException(Exception ex) {
if (log.isDebugEnabled()) {
log.warn("An unexpected error occurred: {}", ex.getMessage(), ex);
} else {
log.warn("An unexpected error occurred: {}", ex.getMessage());
}
BodyBuilder builder;
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
builder = ResponseEntity.status(responseStatus.value());
} else {
builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
}
return builder.body(..); //返回自定义的格式
}
}