Обрабатывать один и тот же URL с помощью Spring MVC RequestMappingHandlerMapping и Spring Websocket ServletWebSocketHandlerRegistry

Что я хочу иметь:

  • Клиент отправляет 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-клиент (браузер), он отвечает Can "Upgrade" only to "WebSocket". Можно ли как-то заменить эту страницу с ошибкой на мое сопоставление MVC? Любые другие конфигурации, чтобы сделать то, что я описал первым?

Ответ 1

Проблема заключается в том, что когда контроллер MVC имеет приоритет, он всегда ответить HTTP 200, обработчик WebSocket никогда не был достигнут

Когда RequestMappingHandlerMapping имеет приоритет над WebSocketHandlerMapping, для запроса конечной точки, которую могут обрабатывать оба из них (если вы просто рассматриваете 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 для определения правильного обработчика для выполнения запроса.