使用Spring MVC的RequestMappingHandlerMapping和Spring Websocket的ServletWebSocketHandlerRegistry处理相同的URL。

14

我想要什么:

  • 客户端发送 GET / HTTP/1.1(不含Connection: upgrade)- 这个请求应该由 RequestMappingHandlerMapping 处理
  • 客户端发送带有 Connection: upgrade 的 GET 请求 - 这个请求应该由 ServletWebSocketHandlerRegistry 处理

我的 Java 配置:

@Configuration
@EnableWebSocket
public class WebsocketConfiguration extends WebMvcConfigurationSupport 
                                    implements WebSocketConfigurer {
    @Bean
    WebsocketComponent wsHandler() {
        return new WebsocketComponent();
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(wsHandler(), "/").setAllowedOrigins("*");
    }
}

我的WebMVC控制器:

@Controller
public class Status {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String status() {
        return "OK";
    }
}

问题在于 - 当MVC控制器优先时,它总是响应HTTP 200,并且WebSocket处理程序永远不会被触发。当WebSocket处理程序优先时 - 它可以与WebSocket客户端一起工作,但是当我尝试使用HTTP客户端(浏览器)时,它会响应只能将“Upgrade”升级为“WebSocket”的错误消息。

有没有可能用什么方式将此错误页面替换为回退到我的MVC映射?还有其他配置可以实现我上述所描述的内容吗?


你确定Spring MVC的请求映射比WebSocket映射优先级低吗?例如,你能在ws://localhost:8080/status上打开一个WebSocket连接吗? - Ali Dehghani
@AliDehghani 当然,WebSocket连接可以使用,但是普通的HTTP连接无法使用,因为出现了以下错误:00:30:20 错误 握手失败,原因是升级标头无效:null。 - vitalyster
显然,你的WebSocketHandlerMapping比你的RequestMappingHandlerMapping更有优先级。通常情况下是相反的,请更多地发布关于你的配置的内容。 - Ali Dehghani
@AliDehghani 当 RequestMappingHandlerMapping 优先时,根本没有回退的位置,请参见更新的问题。 - vitalyster
2
出于好奇,为什么您想要在同一个URL上同时使用它们?为什么不为WebSockets提供一些不同的URI呢? - Aleksandr M
1
@AleksandrM 1) 这是我的遗留应用程序的工作方式,我想使用spring-websockets代替手写的websocket服务器,并保持与客户端的兼容性。2) 最好提供更多带有说明的描述性消息,而不是晦涩的“只能升级到websocket”。 - vitalyster
1个回答

14

问题在于:当MVC控制器优先时,它总是响应HTTP 200,WebSocket处理程序从未被触及

RequestMappingHandlerMappingWebSocketHandlerMapping更优先时,对于两者都可以处理的端点请求(仅考虑URL),DispatcherServlet会将请求分发到@RequestMapping方法而不是WebSocket处理程序。为了解决这个问题,限制@RequestMapping方法只服务于没有Connection: Upgrade头的请求:

@Controller
public class Status {
    @RequestMapping(value = "/", method = GET, headers = "Connection!=Upgrade")
    public String status() {
        return "OK";
    }
}

这样,当DispatcherServlet搜索通用端点的处理程序时,它将考虑是否存在Connection:Upgrade标头来确定正确的处理程序以满足请求。


很好的解释! 在我的情况下,我不得不将测试加倍:headers = {"Connection!=upgrade", "Connection!=Upgrade" } - etienne-sf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接