Spring WebSocket生产环境HttpMessageNotWritableException告警排查咨询
Spring-WebSocket生产环境错误响应Content-Type不匹配告警排查与疑问
问题背景
我用Spring-WebSocket搭建了一套消息系统,开发环境无报错告警,但生产环境出现如下告警:
2022-10-17 13:24:32.894 WARN 1 --- [nio-8080-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved[org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'application/javascript;charset=UTF-8']
排查过程
不清楚告警触发场景与原因,谷歌搜索未找到有效解答,遂将日志级别改为trace排查,发现告警源于向已关闭的会话发送消息时,DispatcherServlet收到类型为INCLUDE的POST "/error"请求。
已知Spring提供BasicErrorController Bean处理"/error"请求,该控制器有两个映射:一个对应HTML类型,一个对应其他类型。错误请求未匹配HTML映射,通过DefaultErrorAttributes返回LinkedHashMap作为响应体,进而引发告警。
为验证猜想,继承BasicErrorController并重写error方法添加日志:
@Slf4j @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class DefaultErrorController extends BasicErrorController { public DefaultErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) { super(errorAttributes, serverProperties.getError(), errorViewResolvers); } @Override @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { log.error(request.getRequestURI()); log.error(request.getDispatcherType().name()); HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL)); return new ResponseEntity<>(body, status); } }
得到以下日志:
2022-10-18 15:35:51.400 TRACE 1 --- [io-8080-exec-19] o.s.web.servlet.DispatcherServlet : "INCLUDE" dispatch for POST "/error?t=1666074896571", parameters={t:[1666074896571]}, headers={masked} in DispatcherServlet 'dispatcherServlet' 2022-10-18 15:35:51.400 TRACE 1 --- [io-8080-exec-19] s.w.s.m.m.a.RequestMappingHandlerMapping : 2 matching mappings: [{ [/error]}, { [/error], produces [text/html]}] 2022-10-18 15:35:51.400 TRACE 1 --- [io-8080-exec-19] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to c.b.w.c.exception.DefaultErrorController#error(HttpServletRequest) 2022-10-18 15:35:51.400 ERROR 1 --- [io-8080-exec-19] c.b.w.c.e.DefaultErrorController : /socket/works/endpoint/694/ksn1ppli/xhr_streaming 2022-10-18 15:35:51.400 ERROR 1 --- [io-8080-exec-19] c.b.w.c.e.DefaultErrorController : INCLUDE 2022-10-18 15:35:51.401 WARN 1 --- [io-8080-exec-19] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'application/javascript;charset=UTF-8'] 2022-10-18 15:35:51.401 TRACE 1 --- [io-8080-exec-19] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned. 2022-10-18 15:35:51.401 DEBUG 1 --- [io-8080-exec-19] o.s.web.servlet.DispatcherServlet : Exiting from "INCLUDE" dispatch, status 200, headers={masked}
推测Socket错误要求返回Content-Type: application/javascript;charset=UTF-8的响应,但Spring默认的MappingJackson2HttpMessageConverter不支持该MIME类型,因此触发告警。
临时解决尝试
修改BasicErrorController,针对特定场景返回空响应:
@Slf4j @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class DefaultErrorController extends BasicErrorController { public static final String SOCKET_ERROR = "xhr_streaming"; public DefaultErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) { super(errorAttributes, serverProperties.getError(), errorViewResolvers); } @Override @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { if(request.getDispatcherType().equals(DispatcherType.INCLUDE) && request.getRequestURI().endsWith(SOCKET_ERROR)) { return new ResponseEntity<>(null, HttpStatus.NOT_FOUND); } HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL)); return new ResponseEntity<>(body, status); } }
该修改使原告警消失,但次日出现新告警:
2022-10-19 09:32:36.485 WARN 1 --- [io-8080-exec-18] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'text/event-stream;charset=UTF-8']
疑问
- 发送事件错误时,哪个类决定响应的Content-Type?
- 如何彻底解决该问题?
内容的提问来源于stack exchange,提问作者Jongmin Yoon
相关产品推荐
相关产品推荐

