OpenFeign调用自定义异常处理
开发中,A服务调用B服务时,B服务中参数校验未通过抛出了自定义异常,错误码是10001,错误消息时“XXXXX不能为空”,返回到A服务时,A服务无法获取到错误码10001。OpenFeign的FeignException返回的异常信息默认status为500。导致10001code丢失。每个服务的自定义异常可能不一样,不过无所谓,主要是异常时获取code和message并透传到前端。自定义异常时返
·
问题说明
开发中,A服务调用B服务时,B服务中参数校验未通过抛出了自定义异常,错误码是10001,错误消息是“XXXXX不能为空”,返回到A服务时,A服务的feign异常拦截无法获取到错误码10001。
OpenFeign的FeignException返回的异常信息默认status为500。导致10001code丢失。
自定义的业务处理异常
每个服务的自定义异常可能不一样,不过无所谓,主要是异常时获取code和message并透传到前端。
@Data
public class BizException extends RuntimeException implements Serializable {
private static final long serialVersionUID = 1L;
private String code;
private String message;
public BizException(){}
/**
* 抛出时,需指定错误码和错误消息
* @param code
* @param message
*/
public BizException(String code, String message) {
super();
this.code = code;
this.message = message;
}
}
自定义异常时返回的错误信息
这是发生异常时,统一返回的数据实体。放在common包下,每个服务公用。
@Data
public class ExceptionInfo {
/**
* 异常时间
*/
private String timestamp;
/**
* 自定义异常码
*/
private String code;
/**
* 自定义异常消息
*/
private String message;
/**
* 异常url
*/
private String path;
}
创建Feign异常响应拦截
它也是Http,和处理controller全局异常一样。
假设现在有个包叫feignimpl,所有的feign接口实现都在这个包下。
这个是"被调用方"处理本服务如果发生了异常,返回ExceptionInfo实体的json数据。
上面的例子B服务就是被调用方。
@Slf4j
@RestControllerAdvice({"com.demo.center.feignimpl"})
public class FeignExceptionHandler {
//抛出的异常可能是自定义异常,也可能是其他运行时异常
@ResponseBody
@ExceptionHandler(value = {Exception.class})
public ExceptionInfo handleFeignStatusException(Exception e, HttpServletRequest request, HttpServletResponse response) {
log.warn(e.getMessage(), e);
//必须要设置response的status。不是200就行。
//比如统一约定服务间调用异常为555错误码
response.setStatus(555);
//如果是自定义业务异常
if (e instanceof BizException) {
BizException bize = (BizException) e;
//构建返回实体
ExceptionInfo ei = new ExceptionInfo();
//异常时间
ei.setTimestamp("时间随便想怎么写就怎么写");
//自定义的错误码
ei.setCode(bize.getCode());
//自定义的错误消息提示
ei.setMessage(bize.getMessage());
//请求的URI
ei.setPath(request.getRequestURI());
return ei;
} else if (e instanceof UserException){
//如果有其他的自定义异常,在这里添加即可
}
//如果是其他的运行时异常,可以统一返回"系统异常,请稍后重试"
//或者报警、邮件等其他处理方式
ExceptionInfo ei = new ExceptionInfo();
ei.setTimestamp("时间随便想怎么写就怎么写");
ei.setCode("111111");
ei.setMessage("系统异常,请稍后重试");
ei.setPath(request.getRequestURI());
return ei;
}
}
创建Feign异常拦截
这个是调用方的异常拦截,上面的例子A服务时调用方。
@Slf4j
@Configuration
public class ExceptionErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String s, Response response){
try {
if (response.body() != null) {
//会把异常信息转换成字符串,注意断点不要打在这一行,会报IO异常
//断点可以打在它的下一行
String body = Util.toString(response.body().asReader(Charset.defaultCharset()));
//将字符串转换为自定义的异常信息
ExceptionInfo ei = GsonUtil.jsonStrToObj(body, ExceptionInfo.class);
//返回异常信息,随便返回哪个异常都行,主要是将code和message透传到前端
return new BizException(ei.getCode(), ei.getMessage());
}
} catch (Exception ex){
//异常记录日志
log.warn("Feign异常处理错误:", ex);
}
//默认返回"系统异常,请稍后重试"
return new BizException("500", "系统异常,请稍后重试");
}
}
最后是返回异常到前端
这个主要是拦截controller层的异常了。
@Slf4j
@RestControllerAdvice({"com.demo.center.controller"})
public class GlobalJsonExceptionController {
/**
* ResponseBody 的controller 统一处理异常 自定义异常
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(value = {Exception.class})
public Response exception(Exception e) {
log.warn(e.getMessage(), e);
if (e instanceof IllegalArgumentException) {
return Response.buildFailed(ResultCode.ILLEGAL_PARAM.getCode(),
ResultCode.ILLEGAL_PARAM.getDesc());
} else if (e instanceof BizException) {
return Response.buildFailed(((BizException) e).getCode(), e.getMessage());
} else if (e instanceof MethodArgumentNotValidException) {
BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
List<FieldError> errors = bindingResult.getFieldErrors();
//拼接message
StringJoiner sj = new StringJoiner(",");
for (FieldError error : errors) {
sj.add(error.getDefaultMessage());
}
return Response.buildFailed("400", sj.toString());
} else {
return Response.buildFailed("500", "系统异常,请稍后重试");
}
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)