You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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']

疑问

  1. 发送事件错误时,哪个类决定响应的Content-Type?
  2. 如何彻底解决该问题?

内容的提问来源于stack exchange,提问作者Jongmin Yoon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 10:20:36