统一异常捕捉

场景

在Controller接口抛出了异常,但接口内没捕捉抛给框架了,框架统一对这类错误处理;也可能是spring boot调度接口出问题,没进入到接口异常,比如接口参数转换失败或服务器找不到。

解决方案

这里有三种

ZUUL拦截

参考:

Spring Cloud实战小贴士:Zuul统一异常处理(一)

Spring Cloud实战小贴士:Zuul统一异常处理(三)【Dalston版】

这种只能处理网关能获取到的异常,对于别的微服务不能捕获到,再一个就是返回的格式不符合自己要的格式;

异常页面跳转

SpringBoot在页面
发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径

application.yaml

server:
  port: 8080
  error:
    path: /custom/error

这里我们使用spring boot原生的,默认的错误处理

 

异常处理Controller类(VndErrorController)

@Controller
public class VndErrorController implements ErrorController {

    private Logger logger = LoggerFactory.getLogger(VndErrorController.class);
    @Value("${error.path:/error}")
    private String errorPath;
    @Autowired
    private ErrorAttributes errorAttributes;

    @Override
    public String getErrorPath() {
        return errorPath;
    }

    @RequestMapping(value = "${error.path:/error}", produces = "application/vnd.error+json")
    public @ResponseBody
    ResponseEntity error(HttpServletRequest request) {

        Map<String,Object> errorAttributes = getErrorAttributes(request, false);
        Integer errrStatus=(Integer)errorAttributes.get("status");
        String path=(String)errorAttributes.get("path");
        String messageFound=(String)errorAttributes.get("message");
        String message="";
        if(!StringUtils.isEmpty(path)){
            message=String.format("Requested path %s with result %s",path,messageFound);
        }
        logger.error("请求失败:{},status={} ",message,errrStatus);

        CommonReturn errorVM = new CommonReturn();
        final int status = getErrorStatus(request);
        switch (status) {
            case 500:  //HttpStatus.INTERNAL_SERVER_ERROR
                errorVM.setRetCode(ErrorCode.SERVER_ERROR);
                break;
            case 408:  //REQUEST_TIMEOUT
            case 504: //GATEWAY_TIMEOUT
                errorVM.setRetCode(ErrorCode.REQUEST_TIMEOUT);
                break;
            case 405:  //METHOD_NOT_ALLOWED
            case 401:  //UNAUTHORIZED
            case 403:  //FORBIDDEN
                errorVM.setRetCode(ErrorCode.HTTP_REQUEST_ILLEGALITY);
                break;
            case 404:  //NOT_FOUND
                errorVM.setRetCode(ErrorCode.REQUEST_NOT_FOUND);
                break;
            case 400:  //BAD_REQUEST
                errorVM.setRetCode(ErrorCode.HTTP_REQUEST_FAIL);
                break;
            default:
                errorVM.setRetCode(ErrorCode.HANDLE_FAIL);
        }
        errorVM.setRetMsg(ErrorCode.getText(errorVM.getRetCode()));
        ResponseEntity.BodyBuilder builder;
        builder = ResponseEntity.status(status);
        return builder.body(errorVM);
    }

    private int getErrorStatus(HttpServletRequest request) {
        Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
        return statusCode != null ? statusCode : HttpStatus.INTERNAL_SERVER_ERROR.value();
    }

    private String getErrorMessage(HttpServletRequest request) {
        final Throwable exc = (Throwable) request.getAttribute("javax.servlet.error.exception");
        return exc != null ? exc.getMessage() : "Unexpected error occurred";
    }

    private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
        RequestAttributes requestAttributes = new ServletRequestAttributes(request);
        return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
    }
}

spring mvc异常统一处理(ControllerAdvice注解)

这种方式我只在网关使用了,其它微服务器也是可以使用的

@ControllerAdvice没有捕获的异常,ErrorController会帮你“捡起来”,像404这类错误@ControllerAdvice没法捕捉

@ControllerAdvice
public class ExceptionTranslator {

    private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);

    @ExceptionHandler(ConcurrencyFailureException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    @ResponseBody
    public Object processConcurrencyError(ConcurrencyFailureException ex) {
        log.warn("An ConcurrencyError occurred: {}", ex);
		return ..;  //返回自定义的格式
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public Object processValidationError(MethodArgumentNotValidException ex) {
        log.warn("An ValidationError error occurred: {}", ex);
        return ..;  //返回自定义的格式
    }

    @ExceptionHandler(CustomParameterizedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public Object processParameterizedValidationError(CustomParameterizedException ex) {
        log.warn("An ParameterizedValidationError occurred: {}", ex);
       return ..;  //返回自定义的格式
    }

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ResponseBody
    public Object processAccessDeniedException(AccessDeniedException e) {
        log.debug("An AccessDeniedException error occurred: {}", e);
       return ..;  //返回自定义的格式
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Object processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
        log.warn("An MethodNotSupportedException error occurred: {}", exception);
        return ..;  //返回自定义的格式
    }

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> processException(Exception ex) {
        if (log.isDebugEnabled()) {
            log.warn("An unexpected error occurred: {}", ex.getMessage(), ex);
        } else {
            log.warn("An unexpected error occurred: {}", ex.getMessage());
        }
        BodyBuilder builder;
        ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
        if (responseStatus != null) {
            builder = ResponseEntity.status(responseStatus.value());
        } else {
            builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
        }
		return builder.body(..);  //返回自定义的格式
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值