全局异常处理

原先的全剧异常处理没有对responsestatus进行设置。导致虽然是返回体抛出了错误的code码。但是Http Status还是200的状态。

原有逻辑如下:

@ExceptionHandler(Exception.class)
@ResponseBody
public ResultVO defaultExceptionHandler(Exception e){
        log.error("异常,{}", e.getMessage(), e);
        if(e instanceof BusinessException) {
            BusinessException businessException = (BusinessException)e;
            if (businessException.getCode().intValue() == 90001){
                //90001异常编号返回成功,提交定单
                Map<String, Object> m = new HashMap<>();
                m.put("type", 2);
                m.put("msg", businessException.getMessage());
                return ResultUtils.success(m);
            }
            return ResultUtils.error(businessException.getCode(), businessException.getMessage());
        }
  	// ...
    }

上面的代码,对返回的请求异常进行捕获,并写入了一个统一包装的ResultVO中。

注入的codemsg内容皆为异常抛出的codemsg

但在实际的返回给上游服务或者是前端时。HTTP Status 始终是200。从请求状态来看始终是没有异常的

改变后如下:

@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AccessDeniedException.class)
public ResultVO<?> accessDeniedExceptionHandler(AccessDeniedException e) {
    log.error("授权异常: {}", e.getMessage(), e);
    return ResultUtils.error(ResultEnum.UNAUTHORIZED.getCode(), e.getMessage());
}

当我需要对认证异常进行捕获并统一封装时,我可以像上面这么写,并且指定返回的Http Status401

异常的捕获处理完成,并返回ResultVO之后,在上游服务或者是Http 请求显示的状态就为401

并且可以定义多个这样的处理,如下:

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({ServiceException.class, BusinessException.class})
public ResultVO<?> throwableHandler(Throwable ex) {
    log.error("业务异常: {}", ex.getMessage(), ex);
    if (ex instanceof ServiceException serviceException) {
        return ResultUtils.error(serviceException.getCode(), serviceException.getMessage());
    }
    if (ex instanceof BusinessException businessException) {
        return ResultUtils.error(businessException.getCode(), businessException.getMessage());
    }
    return ResultUtils.error(ex.getMessage());
}

@ExceptionHandler中指定多个需要抓取的异常进行处理。

并使用@ResponseStatus()这个注解对当前异常需要返回的Http Status Code进行定义

详细的Http Status Code可以参考Spring中的下面这个类

org.springframework.http.HttpStatus

如此一来,当我需要对OpenFeign远程调用进行异常传递的时候,就可以进行处理

OpenFeign 异常处理

原有逻辑的Exception Handler虽然捕获了异常并封装了。但是实际的Http Status始终是200

当自定义实现FeignErrorDecoder接口时,并不会捕获到异常。

如果我们在全局异常捕获处理中,对异常的状态捕获完成之后,并给定了非200的状态,自定义的Feign异常解码就会生效

@Slf4j
@Configuration
public class FeignErrorDecoder implements ErrorDecoder {
    @Override
    public Exception decode(String methodKey, Response response) {
        String url = response.request().url();
        if (response.status() == 404) {
            return new BusinessException("远程服务接口不存在:" + url);
        }
        try {
            String errorContent = Util.toString(response.body().asReader());
            log.error("调用远程:{}异常, errorContent:{}", url, errorContent);
 
            ResultVO resultVO = JSONUtils.string2Obj(errorContent, ResultVO.class);
            throw new BusinessException(resultVO.getMsg());
        } catch (IOException e) {
            log.error("处理 FeignClient 异常错误", e);
            return new RuntimeException("处理FeignClient 异常错误");
        }
    }
}