OpenFeign调用自定义异常处理

2022年7月26日11:17:37

问题说明

开发中,A服务调用B服务时,B服务中参数校验未通过抛出了自定义异常,错误码是10001,错误消息是“XXXXX不能为空”,返回到A服务时,A服务的feign异常拦截无法获取到错误码10001。
OpenFeign的FeignException返回的异常信息默认status为500。导致10001code丢失。

自定义的业务处理异常

每个服务的自定义异常可能不一样,不过无所谓,主要是异常时获取code和message并透传到前端。

@DatapublicclassBizExceptionextendsRuntimeExceptionimplementsSerializable{privatestaticfinallong serialVersionUID=1L;privateString code;privateString message;publicBizException(){}/**
     * 抛出时,需指定错误码和错误消息
     * @param code
     * @param message
     */publicBizException(String code,String message){super();this.code= code;this.message= message;}}

自定义异常时返回的错误信息

这是发生异常时,统一返回的数据实体。放在common包下,每个服务公用。

@DatapublicclassExceptionInfo{/**
     * 异常时间
     */privateString timestamp;/**
     * 自定义异常码
     */privateString code;/**
     * 自定义异常消息
     */privateString message;/**
     * 异常url
     */privateString path;}

创建Feign异常响应拦截

它也是Http,和处理controller全局异常一样。
假设现在有个包叫feignimpl,所有的feign接口实现都在这个包下。
这个是"被调用方"处理本服务如果发生了异常,返回ExceptionInfo实体的json数据。
上面的例子B服务就是被调用方。

@Slf4j@RestControllerAdvice({"com.demo.center.feignimpl"})publicclassFeignExceptionHandler{//抛出的异常可能是自定义异常,也可能是其他运行时异常@ResponseBody@ExceptionHandler(value={Exception.class})publicExceptionInfohandleFeignStatusException(Exception e,HttpServletRequest request,HttpServletResponse response){
		log.warn(e.getMessage(), e);//必须要设置response的status。不是200就行。//比如统一约定服务间调用异常为555错误码
		response.setStatus(555);//如果是自定义业务异常if(einstanceofBizException){BizException bize=(BizException) e;//构建返回实体ExceptionInfo ei=newExceptionInfo();//异常时间
            ei.setTimestamp("时间随便想怎么写就怎么写");//自定义的错误码
            ei.setCode(bize.getCode());//自定义的错误消息提示
            ei.setMessage(bize.getMessage());//请求的URI
            ei.setPath(request.getRequestURI());return ei;}elseif(einstanceofUserException){//如果有其他的自定义异常,在这里添加即可}//如果是其他的运行时异常,可以统一返回"系统异常,请稍后重试"//或者报警、邮件等其他处理方式ExceptionInfo ei=newExceptionInfo();
        ei.setTimestamp("时间随便想怎么写就怎么写");
        ei.setCode("111111");
        ei.setMessage("系统异常,请稍后重试");
        ei.setPath(request.getRequestURI());return ei;}}

创建Feign异常拦截

这个是调用方的异常拦截,上面的例子A服务时调用方。

@Slf4j@ConfigurationpublicclassExceptionErrorDecoderimplementsErrorDecoder{@OverridepublicExceptiondecode(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透传到前端returnnewBizException(ei.getCode(), ei.getMessage());}}catch(Exception ex){//异常记录日志
			log.warn("Feign异常处理错误:", ex);}//默认返回"系统异常,请稍后重试"returnnewBizException("500","系统异常,请稍后重试");}}

最后是返回异常到前端

这个主要是拦截controller层的异常了。

@Slf4j@RestControllerAdvice({"com.demo.center.controller"})publicclassGlobalJsonExceptionController{/**
     * ResponseBody 的controller 统一处理异常 自定义异常
     * @param e
     * @return
     */@ResponseBody@ExceptionHandler(value={Exception.class})publicResponseexception(Exception e){
        log.warn(e.getMessage(), e);if(einstanceofIllegalArgumentException){returnResponse.buildFailed(ResultCode.ILLEGAL_PARAM.getCode(),ResultCode.ILLEGAL_PARAM.getDesc());}elseif(einstanceofBizException){returnResponse.buildFailed(((BizException) e).getCode(), e.getMessage());}elseif(einstanceofMethodArgumentNotValidException){BindingResult bindingResult=((MethodArgumentNotValidException) e).getBindingResult();List<FieldError> errors= bindingResult.getFieldErrors();//拼接messageStringJoiner sj=newStringJoiner(",");for(FieldError error: errors){
                sj.add(error.getDefaultMessage());}returnResponse.buildFailed("400", sj.toString());}else{returnResponse.buildFailed("500","系统异常,请稍后重试");}}}
  • 作者:小太阳〃
  • 原文链接:https://blog.csdn.net/lmq2582609/article/details/125131922
    更新时间:2022年7月26日11:17:37 ,共 3388 字。