> 文章列表 > No message available问题解决

No message available问题解决

No message available问题解决

概述

在EFK日志查询平台断断续续看到若干个应用的报错信息:
No message available问题解决
No message available问题解决

排查

上述截图里报错的类(省略掉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);}@Overridepublic 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问题解决
控制台打印信息如下: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-provideruri: http://cbspredicates:# - Path=/cbs/filters:- StripPrefix=1

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

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