赞
踩
原理基于请求头传递错误消息,利用aop和全局异常拦截机制实现。
/** * 全局异常处理器 */ @RestControllerAdvice @Configuration @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) public Object defaultErrorHandler(Exception e) throws Exception { log.error("系统异常:{}", e); return ResultUtils.error("系统异常", String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value())); } /** * 处理运行时异常 * * @param ex * @return */ @ExceptionHandler(value = RuntimeException.class) public Object runtimeException(RuntimeException ex) { log.error("系统异常:{}", ex); return ResultUtils.error("系统异常!", String.valueOf(HttpStatus.EXPECTATION_FAILED.value())); } @ExceptionHandler(value = DecodeException.class) public Object decodeException(DecodeException ex) { log.error("系统异常:{}", ex); return ResultUtils.error(ex.getMessage(), String.valueOf(ex.status())); } /** * 处理自定义业务异常 * * @param ex * @return */ @ExceptionHandler(value = BusinessException.class) public Object exceptionHandler(BusinessException ex) { log.error("业务异常详情:{}", ex); return ResultUtils.error(ex.getMessage(), ex.getCode()); } @ExceptionHandler(value = UserContextLoseException.class) public Object notLoginException(UserContextLoseException e) { log.error("当前用户信息不存在异常:{}", e); return ResultUtils.notLogin(); } /** * 方法入参校验异常处理 content-type!=application/json * * @param ex 异常 * @return Object */ @ExceptionHandler({BindException.class}) public Object bindExceptionException(BindException ex) { List<String> validResult = ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList()); log.warn("参数非法:{}", ex); return ResultUtils.error(validResult.get(0)); } /** * 方法入参校验异常处理 content-type=application/json * * @param ex 异常 * @return Object */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) { List<String> validResult = ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList()); log.warn("参数非法:{}", ex); // TODO 需要特殊处理异常,否则微服务接口被@Validated校验未通过时无法抛出对应的异常信息 return ResultUtils.error(validResult.get(0)); } /** * 请求类型不支持 * * @param httpRequestMethodNotSupportedException * @return */ @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) public Object handleNotSupportedHttpMethodException(HttpRequestMethodNotSupportedException httpRequestMethodNotSupportedException) { log.error("HttpRequestMethodNotSupportedException:{}", httpRequestMethodNotSupportedException); return ResultUtils.error(httpRequestMethodNotSupportedException.getMessage(), String.valueOf(HttpStatus.EXPECTATION_FAILED.value())); } /** * media type not support * * @param httpMediaTypeNotSupportedException * @return */ @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class) @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) public Object handleNotSupportedHttpMethodException(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) { log.error("HttpMediaTypeNotSupportedException:{}", httpMediaTypeNotSupportedException); return ResultUtils.error(httpMediaTypeNotSupportedException.getMessage(), String.valueOf(HttpStatus.EXPECTATION_FAILED.value())); } }
@Slf4j @Aspect @Order(value = 100) @Component public class FeignExceptionAspect { @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" + " || @annotation(org.springframework.web.bind.annotation.GetMapping)" + " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) { try { Object proceed = joinPoint.proceed(); return proceed; } catch (BusinessException e) { log.error("feign调用异常:{}", e.getMessage()); if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode()); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8")); } throw e; } catch (UserContextLoseException e) { log.error("用户信息缺失:{}", e); if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode()); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8")); } throw e; } catch (FeignException e) { // 存在未发起远程调用前抛出的FeignException异常 if (e instanceof FeignException.ServiceUnavailable) { FeignException.ServiceUnavailable serviceUnavailable = (FeignException.ServiceUnavailable) e; log.error(serviceUnavailable.getMessage()); throw BusinessException.createException("服务不可用"); } throw e; } catch (Throwable throwable) { Throwable cause = throwable.getCause(); while (null != cause && null != cause.getCause()) { cause = cause.getCause(); } if (cause instanceof BusinessException) { if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((BusinessException) cause).getCode()); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8)); } throw (BusinessException) cause; } else if (cause instanceof UserContextLoseException) { if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((UserContextLoseException) cause).getCode()); response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8)); } throw (UserContextLoseException) cause; } log.error("接口调用异常:{}", throwable); throw new RuntimeException("未知异常"); } } }
@Slf4j public class FeignExceptionClient implements Client { @Override public Response execute(Request request, Request.Options options) throws IOException { Response response = new ApacheHttpClient().execute(request, options); RequestTemplate requestTemplate = response.request().requestTemplate(); Target<?> target = requestTemplate.feignTarget(); String serviceName = target.name(); Class<?> type = target.type(); String api = type.getName(); String url = requestTemplate.url(); Collection<String> errorCodes = response.headers().get(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY); Collection<String> errormessage = response.headers().get(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY); String errorMessage = null; if (null != errormessage && !errormessage.isEmpty()) { errorMessage = (String) ((List) errormessage).get(0); errorMessage = Base64.decodeStr(errorMessage, CommonConsts.UTF8); } if (CollectionUtils.isNotEmpty(errorCodes)) { logInvokeError(serviceName, api, url); Object errorCode = ((List) errorCodes).get(0); if (ResultConsts.NOT_LOGIN.toString().equals(errorCode)) { throw UserContextLoseException.createException(); } if (String.valueOf(HttpStatus.EXPECTATION_FAILED.value()).equals(errorCode)) { throw BusinessException.createException("系统异常"); } if (String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()).equals(errorCode)) { throw BusinessException.createException("系统异常"); } if (StringUtils.isNotEmpty(errorMessage)) { throw BusinessException.createException(errorMessage); } throw BusinessException.createException("系统异常"); } if (StringUtils.isNotEmpty(errorMessage)) { logInvokeError(serviceName, api, url); throw BusinessException.createException(errorMessage); } return response; } private void logInvokeError(String serviceName, String api, String method) { log.error("调用微服务[{}]-Api[{}]-Uri[{}]异常", serviceName, api, method); } }
public class FeignDecoder implements Decoder { @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { String typeName = type.getTypeName();// class void if (StringUtils.isNotEmpty(typeName)) { Response.Body body = response.body(); String resultString = Util.toString(body.asReader(Util.UTF_8)); ResponseVO responseVO = JSONObject.parseObject(resultString, ResponseVO.class); if (null != responseVO) { if (null != responseVO.getCode() && !responseVO.getCode().toString().endsWith(String.valueOf(ResultConsts.SUCCESS_STATUS))) { // 2002000100 417 not-login 100 business throw new DecodeException(responseVO.getCode(), responseVO.getMessage(), response.request()); } } Class<?> responseCls; try { responseCls = Class.forName(typeName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return JSONObject.parseObject(resultString, responseCls); } return Util.emptyValueOf(type); } }
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
return new ErrorDecoder.Default().decode(methodKey, response);
}
}
bean注入
@Bean
public Decoder decoder() {
return new FeignDecoder()::decode;
}
@Bean
public ErrorDecoder errorDecoder() {
return new FeignErrorDecoder()::decode;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。