java应用中,调用第三方api如何处理异常(系统异常、操作异常等等)

java应用中,调用第三方api如何处理异常(系统异常、操作异常等等)
         一个应用大多数都要调用其它服务接口获取相关资源,而第三方服务会有很多错误码,有相关系统异常,用户操作异常,授权异常等等。其实对于自己的应用而讲,可以两种:系统异常用户非法操作异常,对应自己项目中的http状态为:    INTERNAL_SERVER_ERROR(500, "Internal Server Error")、    OK(200, "OK")(这里不讨论自身应用的参数校验,spring帮我们做好了)。
 
       一般系统异常是我们开发者比较关注的,因为导致这种异常发生的原因在于代码bug的出现 比如:自身系统的系统异常;第三方接口返回的契约异常:如 传入的参数不符合第三方接口数据格式要求、当前代理客户没有部分或全部航班、舱位的销售权限、取消选座操作异常等等,这类异常一般让提示用户重试或者联系客服,一般联系客服,是客户重试也无法解决的。
        用户非法操作异常 也是比较常见的,比如:订票限制成人至少要有1个,最多只能有9个、组成往返连程的航班必须是同一航空公司的航班、请确认订单中成人旅客证件号码的正确性等等,这些第三方给的用户操作异常信息必须返回给客户,让客户自己处理才能解决。
     系统异常,需要web端或移动端判断http的错误码, 用户非法操作异常需要判断返回的json数据中的returnCode,当returnCode 为0,表示业务成功,非0,表示用户非法操作,需要提示对应 returnMsg。
   
    (注:其实系统异常也可以返回http 200,然后用一个特定的returnCode比如99999表示)。
      为了处理这些异常,项目主要利用对错误码分类,分别抛出对应的自定义异常(便于分析问题原因),利用spring的ControllerAdvice 和aop对这些异常处理。
      特别的,对应第三方接口的参数校验,方法参数使用了注解@Valid,这是spring所支持的,详见官方文档,会抛出ConstraintViolationException异常,为了明确的表明是第三方接口参数校验异常,利用aop,重新包装为自定义异常RemoteServiceMethodArgumentNotValidException抛出。
    
              【 普通方法参数校验,需要对参数加入@Valid注解,如:
 public List getCountryAreaCode(@NotNull LanguageEnum languageEnum)、
  public List searchFlightsBatch(@Valid SearchFlightsBatchSearchBean searchInfo) ;

      SearchFlightsBatchSearchBean 中的属性根据需要加上javax.validation相关包中的注解,属性为对象,要校验必须加上 @Valid注解。】


    
            为了方便处理第三方的异常类型,我们用枚举列出第三方接口约定的异常,我们需要把错误码归类:
/**xxx
 * Created by xxxxx on 2017/5/15.
 */
@Slf4j
@Getter
@ToString
public enum SpringairlineErrCode {

    ERR665_007("ERR665-007", "请确认订单中成人旅客证件号码的正确性"),
    AIRSALESWS_EXCEP_111("AIRSALESWS-EXCEP-111", "当前代理客户没有该订单的操作权限"),

    ERR803_166("ERR803-166", "需要绑定保险"),

    ERR803_074("ERR803-074", "必填参数未填写完整"),
    ERR814_001("ERR814-001", "参数异常"),
    ERR808_999("ERR808-999", "其它异常"),

    ...................................

    UNKNOWNERROR("unknown", "unknown error");

    private String errCode;
    private String errMsg;

    SpringairlineErrCode(String errCode, String errMsg) {
        this.errCode = errCode;
        this.errMsg = errMsg;
    }


    private static Map codeMap = new HashMap<>();

    static {
        for (SpringairlineErrCode code : SpringairlineErrCode.values()) {
            put(codeMap, code);
        }
    }


   
    public static SpringairlineErrCode of(final String errCode) {
        log.error("春秋错误码:{}", errCode);
        SpringairlineErrCode springairlineErrCode = codeMap.get(errCode);
        ...................................

        if (springairlineErrCode == null) {
            log.warn("春秋未知错误码:{}", errCode);
            springairlineErrCode = SpringairlineErrCode.UNKNOWNERROR;
        }
        return springairlineErrCode;
    }


    //用户非法操作异常
    public static boolean isUserIllegalOperation(SpringairlineErrCode code) {
//        return userIllegalOperationCode.contains(code);
        return !sysException.contains(code);
    }


    //系统异常
    private static Set sysException = new HashSet<>();

    static {
        put(sysException, AIRSALESWS_EXCEP_111);
                ,,,,,,,

);
        
    }

    private static void put(Map map, SpringairlineErrCode code) {
        map.put(code.getErrCode(), code);
    }

    private static void put(Set set, SpringairlineErrCode code) {
        set.add(code);
    }

}

  枚举中用
sysException 表示所有系统异常,其他的就是用户操作异常。
 
  为了对接口的返回值处理我们需要定义一个工具类:
SpringairlineResponseUtil
   
  
 private static void handle(String errCode) {
        SpringairlineErrCode errCode = SpringairlineErrCode.of(errCode);
        if (SpringairlineErrCode.isUserIllegalOperation(errCode)) {
            throw new UserIllegalOperationException(errCode.getErrCode(), errCode.getErrMsg());
        }
        throw new RemoteServiceException(errCode.getErrCode(), errCode.getErrMsg());
    }

      如果接口返回的错误码是用户操作异常,我们抛出自定义异常:UserIllegalOperationException,其他抛出自定义异常RemoteServiceException.

  为了对这些异常处理,需用使用spring的RestControllerAdvice处理,

/**
 * Created by xxxxxxxx on 2017/6/13.
 * 

* 异常处理,rest端 */ @RestControllerAdvice @Slf4j public class ExceptionResponseHandlerConfig { /** * 用户操作异常提示,展现给客户端 * * @param ex * @param request * @return */ @ExceptionHandler(UserIllegalOperationException.class) public ResponseEntity handleSysException(UserIllegalOperationException ex, WebRequest request) { ModelResult modelResult = new ModelResult<>(); modelResult.setReturnCode(ex.getErrorCode()); modelResult.setReturnMsg(ex.getErrorMessage()); log.error("handleException:{}", modelResult, ex); return new ResponseEntity<>(modelResult, HttpStatus.OK); } }   
为了更好的分析问题,我们对第三方接口的参数要求也用了spring的@Valid注解,sping会抛出ConstraintViolationException异常,为了方便定位是第三方接口参数校验异常,用aop 方法抛出自定义异常:RemoteServiceMethodArgumentNotValidException,RestControllerAdvice会当做内部服务器异常处理。

  
 @Around(("pointcutSpringairline() "))
    public Object aroundSampleCreation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String argss = null;
        Object[] args = proceedingJoinPoint.getArgs();
        if (args != null) {
            ObjectMapper objectMapper = new ObjectMapper();
            argss = objectMapper.writeValueAsString(args);
        }

        log.info("执行方法:{}  参数:{}", proceedingJoinPoint.getSignature(), argss);
        Object proceed = null;

        try {
            proceed = proceedingJoinPoint.proceed();
        } catch (ConstraintViolationException e) {
            Set> violations = e.getConstraintViolations();
            String message = violations.stream().map(t -> t.getMessage()).collect(joining(","));
            throw new RemoteServiceMethodArgumentNotValidException(message + " ============>>>>>> " + violations.toString(), e);
        }


        return proceed;
    }

     
       当spring RestControllerAdvice 对异常做了处理后,系统异常会以INTERNAL_SERVER_ERROR(500, "Internal Server Error")给予客户端提示,用户非法操作异常OK(200, "OK")返回,当返回http OK(200, "OK")时,客户端还需要判断ModelResult对象中的
    /**
     * 0 代表成功,其余自定义错误码
     */
    private String returnCode = "0";
    /**
     * 错误信息描述
     */
    private String returnMsg = "成功!";

  当returnCode为0,代码业务执行成功,非0,需要提示用户的非法操作异常信息为returnMsg内容。

   当第三方接口不仅仅用错误码表示异常,用http状态码时,我们只能对http client 相关的客户端框架做处理,其思路是一样的。
  

   博文的主要思路是对第三方接口的返回错误码分类,分别抛出自定义异常,然后利用spring 的RestControllerAdvice 和aop 功能,对自定义异常进行封装返回数据给客户端处理。



你可能感兴趣的:(Spring,Boot,Spring,Boot,实战)