Spring Cloud zuul custom unified exception handling implementation method

  • 2021-01-22 05:11:28
  • OfStack

Zuul provides filer and router functions in the springcloud microservice architecture, which is an integral part of the microservice. filer handles the default implementation but also can be customized for authorization, current limitation, security verification, etc. router can completely replace Nginx reverse proxy. Zuul exception handling is done by SendErrorFilter.

In our application we found that using the default exception filter has two problems that are not very friendly:

1. Unable to quickly identify whether the request routing service timeout or no available nodes, error can only view the log through the stack to locate;

2. Not compatible with custom examples {code:500,msg:”xx error”} Format response package format.

Next we discuss how to customize exception handling, custom exception prompts, and so on.

First of all, we must disable the default SendErrorFilter. The official switch configuration is provided and can be configured directly


zuul.SendErrorFilter.post.disable=true

Custom ErrorFilter, here is not much to say, directly posted code


public class ErrorFilter extends ZuulFilter {
  private static final String ERROR_STATUS_CODE_KEY = "error.status_code";
  private Logger log = LoggerFactory.getLogger(ErrorFilter.class);
  public static final String DEFAULT_ERR_MSG = " System is busy , Please try again later ";
  @Override
  public String filterType() {
    return "post";
  }
  @Override
  public int filterOrder() {
    return 0;
  }
  @Override
  public boolean shouldFilter() {
    RequestContext ctx = RequestContext.getCurrentContext();
    return ctx.containsKey(ERROR_STATUS_CODE_KEY);
  }
  @Override
  public Object run() {    
    RequestContext ctx = RequestContext.getCurrentContext();
    try {
      HttpServletRequest request = ctx.getRequest();
      int statusCode = (Integer) ctx.get(ERROR_STATUS_CODE_KEY);
      String message = (String) ctx.get("error.message");
      if (ctx.containsKey("error.exception")) {
        Throwable e = (Exception) ctx.get("error.exception");
        Throwable re = getOriginException(e);
        if(re instanceof java.net.ConnectException){
          message = "Real Service Connection refused";
          log.warn("uri:{},error:{}" ,request.getRequestURI(),re.getMessage());
        }else if(re instanceof java.net.SocketTimeoutException){
          message = "Real Service Timeout";
          log.warn("uri:{},error:{}" ,request.getRequestURI(),re.getMessage());
        }else if(re instanceof com.netflix.client.ClientException){
          message = re.getMessage();
          log.warn("uri:{},error:{}" ,request.getRequestURI(),re.getMessage());
        }else{
          log.warn("Error during filtering",e);
        }
      }
      if(StringUtils.isBlank(message))message = DEFAULT_ERR_MSG;
      request.setAttribute("javax.servlet.error.status_code", statusCode);
      request.setAttribute("javax.servlet.error.message", message);
      WebUtils.responseOutJson(ctx.getResponse(), JsonUtils.toJson(new WrapperResponse<>(statusCode, message)));
    } catch (Exception e) {
      String error = "Error during filtering[ErrorFilter]";
      log.error(error,e);
      WebUtils.responseOutJson(ctx.getResponse(), JsonUtils.toJson(new WrapperResponse<>(500, error)));
    }
    return null;
  }
  private Throwable getOriginException(Throwable e){
    e = e.getCause();
    while(e.getCause() != null){
      e = e.getCause();
    }
    return e;
  }
}

Finally, register for our custom ErrorFilter


@Bean 
public ErrorFilter errorFilter(){
  return new ErrorFilter();
}

conclusion


Related articles: