No message available问题解决

概述

在EFK日志查询平台断断续续看到若干个应用的报错信息:
在这里插入图片描述
No message available问题解决_第1张图片

排查

上述截图里报错的类(省略掉Import语句后):

@Slf4j
@RestController
public class FilterErrorController extends BasicErrorController {
    public FilterErrorController() {
        super(new DefaultErrorAttributes(), new ErrorProperties());
    }

    @Override
    @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        // 第35行
        log.error(body.get("message").toString());
        Map<String, Object> responseBody = new HashMap<>(16);
        responseBody.put("code","9000");
        responseBody.put("message","服务器开小差了");
        return new ResponseEntity<>(responseBody, status);
    }

    @Override
    public String getErrorPath() {
        return "error/error";
    }
}

自定义的FilterErrorController继承BasicErrorController,而BasicErrorController则继承AbstractErrorController,继续跟进源码:

public class BasicErrorController extends AbstractErrorController {
}

找到AbstractErrorController.getErrorAttributes()方法:

public abstract class AbstractErrorController implements ErrorController {
    protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
       WebRequest webRequest = new ServletWebRequest(request);
       return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
    }
}

进一步定位到DefaultErrorAttributes.addErrorDetails()

@Order(Integer.MIN_VALUE)
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap();
        errorAttributes.put("timestamp", new Date());
        this.addStatus(errorAttributes, webRequest);
        this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
        this.addPath(errorAttributes, webRequest);
        return errorAttributes;
    }
    
    private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace) {
        Throwable error = this.getError(webRequest);
        if (error != null) {
            while(true) {
                if (!(error instanceof ServletException) || error.getCause() == null) {
                    if (this.includeException) {
                        errorAttributes.put("exception", error.getClass().getName());
                    }    
                    this.addErrorMessage(errorAttributes, error);
                    if (includeStackTrace) {
                        this.addStackTrace(errorAttributes, error);
                    }
                    break;
                }
                error = ((ServletException)error).getCause();
            }
        }
        Object message = this.getAttribute(webRequest, "javax.servlet.error.message");
        if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null) && !(error instanceof BindingResult)) {
            errorAttributes.put("message", StringUtils.isEmpty(message) ? "No message available" : message);
        }
    }    
}

源码是定位到。但是为啥呢?排查无果,搁置一阵子。

后续

后来在本地使用postman调试另一个服务的接口 http://localhost:8099/api/cbs/area/getProvinceList,这个地址URL是从我们一个内部后台管理系统(基于开源管理平台【若依】改造)复制而来,也发生相同的报错:
No message available问题解决_第2张图片
控制台打印信息如下:aba-cbs-provider | [http-nio-8099-exec-4] | | ERROR | com.aba.web.controller.FilterErrorController | error | 35 | - No message available

既然本地可以复现,那就好办。

看了下,项目工程里controller层代码的URL并没有前缀/api,为啥在后台管理系统里要加上/api,推测下来是统一化与规范化处理。

postman请求 http://localhost:8099/api/cbs/area/getProvinceList,正常返回。

gateway请求转发?突然想到之前在Apollo看到的几个gateway相关的配置:

spring.cloud.gateway.routes[24].id = aba-cbs-provider
spring.cloud.gateway.routes[24].uri = lb://aba-cbs-provider
spring.cloud.gateway.routes[24].predicates[0] = Path=/api/cbs/**
spring.cloud.gateway.routes[24].filters[0] = StripPrefix=1

其中有些服务有spring.cloud.gateway.routes[24].filters[0] = StripPrefix=1这个配置项,而有些服务则无。

cbs应用添加spring.cloud.gateway.routes[24].filters[0] = StripPrefix=1配置后,再次通过postman请求接口 http://localhost:8099/api/cbs/area/getProvinceList,报错消失!

理论知识

StripPrefix这个Spring Cloud Gateway配置项作用是啥。搜索官方文档,StripPrefix参数表示在将请求发送到下游之前从请求中剥离的路径个数。

spring:
  cloud:
    gateway:
      routes:
        - id: aba-cbs-provider
          uri: http://cbs
          predicates:
          # - Path=/cbs/**
          filters:
            - StripPrefix=1

当通过网关服务向cbs服务发起api/cbs/area/getProvinceList发出请求时,转发到cbs服务的请求变成cbs/area/getProvinceList

StripPrefix=2时,则会从路径URLapi/cbs/area/getProvinceList里去掉2个前缀层级,即得到area/getProvinceList

你可能感兴趣的:(Spring,Cloud,spring,cloud)