WebSocket API
Spring Framework は、WebSocket メッセージを処理するクライアント側およびサーバー側のアプリケーションを作成するために使用できる WebSocket API を提供します。
WebSocketHandler
WebSocket サーバーの作成は、WebSocketHandler
を実装するのと同じくらい簡単で、おそらくは TextWebSocketHandler
または BinaryWebSocketHandler
のいずれかを継承します。次の例では、TextWebSocketHandler
を使用しています。
Java
Kotlin
public class MyHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
class MyHandler : TextWebSocketHandler() {
override fun handleTextMessage(session: WebSocketSession, message: TextMessage) {
// ...
}
}
次の例に示すように、前述の WebSocket ハンドラーを特定の URL にマッピングするための専用の WebSocket プログラム構成と XML 名前空間サポートがあります。
Java
Kotlin
XML
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
@Configuration
@EnableWebSocket
class WebSocketConfiguration : WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(myHandler(), "/myHandler")
}
@Bean
fun myHandler(): WebSocketHandler {
return MyHandler()
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.docs.web.websocket.websocketserverhandler.MyHandler"/>
</beans>
上記の例は Spring MVC アプリケーションで使用するためのものであり、DispatcherServlet
の構成に含める必要があります。ただし、Spring の WebSocket サポートは Spring MVC に依存しません。WebSocketHttpRequestHandler
(Javadoc) の助けを借りて、WebSocketHandler
を他の HTTP サービス環境に統合するのは比較的簡単です。
WebSocketHandler
API を直接使用する場合と、たとえば STOMP メッセージングを介して間接的に使用する場合、基礎となる標準 WebSocket セッション (JSR-356) では同時送信が許可されていないため、アプリケーションはメッセージの送信を同期する必要があります。1 つのオプションは、WebSocketSession
を ConcurrentWebSocketSessionDecorator
(Javadoc) でラップすることです。
WebSocket ハンドシェイク
最初の HTTP WebSocket ハンドシェイクリクエストをカスタマイズする最も簡単な方法は、ハンドシェイクの「前」と「後」のメソッドを公開する HandshakeInterceptor
を使用することです。このようなインターセプターを使用して、ハンドシェイクを除外したり、WebSocketSession
で属性を使用可能にしたりできます。次の例では、組み込みインターセプターを使用して、HTTP セッション属性を WebSocket セッションに渡します。
Java
Kotlin
XML
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/myHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
@Configuration
@EnableWebSocket
class WebSocketConfiguration : WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(MyHandler(), "/myHandler")
.addInterceptors(HttpSessionHandshakeInterceptor())
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.docs.web.websocket.websocketserverhandler.MyHandler"/>
</beans>
より高度なオプションは、WebSocket ハンドシェイクのステップを実行する DefaultHandshakeHandler
を継承することです。これには、クライアントの発信元の検証、サブプロトコルのネゴシエーション、その他の詳細が含まれます。アプリケーションは、WebSocket サーバーエンジンとまだサポートされていないバージョンに適応するためにカスタム RequestUpgradeStrategy
を構成する必要がある場合、このオプションを使用する必要があります(このテーマの詳細については、デプロイを参照してください)。Java 構成と XML 名前空間の両方により、カスタム HandshakeHandler
を構成できます。
Spring は、WebSocketHandler を追加の動作で装飾するために使用できる WebSocketHandlerDecorator 基本クラスを提供します。WebSocket Java 構成または XML 名前空間を使用する場合、デフォルトでロギングおよび例外処理の実装が提供および追加されます。ExceptionWebSocketHandlerDecorator は、WebSocketHandler メソッドから発生するすべてのキャッチされない例外をキャッチし、サーバーエラーを示すステータス 1011 の WebSocket セッションを閉じます。 |
デプロイ
Spring WebSocket API は、DispatcherServlet
が HTTP WebSocket ハンドシェイクと他の HTTP リクエストの両方に対応する Spring MVC アプリケーションに簡単に統合できます。WebSocketHttpRequestHandler
を呼び出すことで、他の HTTP 処理シナリオに簡単に統合することもできます。これは便利で分かりやすいです。ただし、JSR-356 ランタイムに関しては特別な考慮事項が適用されます。
Jakarta WebSocket API (JSR-356) は、2 つの デプロイメカニズムを提供します。1 つ目は、起動時のサーブレットコンテナークラスパススキャン (Servlet 3 機能) です。もう 1 つは、サーブレットコンテナーの初期化時に使用する登録 API です。これらのメカニズムのどちらも、WebSocket ハンドシェイクや Spring MVC の DispatcherServlet
などの他のすべての HTTP リクエストを含む、すべての HTTP 処理に単一の「フロントコントローラー」を使用することを可能にします。
これは、JSR-356 ランタイムで実行されている場合でも、Spring の WebSocket がサーバー固有の RequestUpgradeStrategy
実装でアドレスをサポートするという JSR-356 の重大な制限です。このような戦略は現在、Tomcat、Jetty、GlassFish、WebLogic、WebSphere、Undertow (および WildFly) に存在します。Jakarta WebSocket 2.1 の時点で、Spring が Tomcat 10.1 や Jetty 12 などの Jakarta EE 10 ベースの Web コンテナーで選択する標準のリクエストアップグレード戦略が利用可能です。
2 番目の考慮事項は、JSR-356 をサポートするサーブレットコンテナーが ServletContainerInitializer
(SCI)スキャンを実行すると予想されることです。これにより、アプリケーションの起動が遅くなる場合があります。JSR-356 をサポートするサーブレットコンテナーバージョンへのアップグレード後に重大な影響が観察された場合、次の例のように、web.xml
の <absolute-ordering />
要素を使用して、Web フラグメント(および SCI スキャン)を選択的に有効または無効にできます。ショー:
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<absolute-ordering/>
</web-app>
次に、Servlet 3 Java 初期化 API のサポートを提供する Spring 独自の SpringServletContainerInitializer
など、名前で Web フラグメントを選択的に有効にできます。次の例は、その方法を示しています。
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<absolute-ordering>
<name>spring_web</name>
</absolute-ordering>
</web-app>
サーバーの構成
入力メッセージバッファサイズ、アイドルタイムアウトなど、基盤となる WebSocket サーバーを構成できます。
Jakarta WebSocket サーバーの場合、構成に ServletServerContainerFactoryBean
を追加できます。例:
Java
Kotlin
XML
@Configuration
public class WebSocketConfiguration {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}
@Configuration
class WebSocketConfiguration {
@Bean
fun createWebSocketContainer() = ServletServerContainerFactoryBean().apply {
maxTextMessageBufferSize = 8192
maxBinaryMessageBufferSize = 8192
}
}
<bean class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean">
<property name="maxTextMessageBufferSize" value="8192"/>
<property name="maxBinaryMessageBufferSize" value="8192"/>
</bean>
クライアント Jakarta WebSocket 構成の場合、プログラム構成では ContainerProvider.getWebSocketContainer() を使用するか、XML では WebSocketContainerFactoryBean を使用します。 |
Jetty の場合、WebSocket サーバーを構成するためのコールバックを提供できます。
Java
Kotlin
@Configuration
@EnableWebSocket
public class JettyWebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").setHandshakeHandler(handshakeHandler());
}
@Bean
public WebSocketHandler echoWebSocketHandler() {
return new MyEchoHandler();
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
JettyRequestUpgradeStrategy strategy = new JettyRequestUpgradeStrategy();
strategy.addWebSocketConfigurer(configurable -> {
configurable.setInputBufferSize(8192);
configurable.setIdleTimeout(Duration.ofSeconds(600));
});
return new DefaultHandshakeHandler(strategy);
}
}
@Configuration
@EnableWebSocket
class JettyWebSocketConfiguration : WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(echoWebSocketHandler(), "/echo").setHandshakeHandler(handshakeHandler())
}
@Bean
fun echoWebSocketHandler(): WebSocketHandler {
return MyEchoHandler()
}
@Bean
fun handshakeHandler(): DefaultHandshakeHandler {
val strategy = JettyRequestUpgradeStrategy()
strategy.addWebSocketConfigurer {
it.inputBufferSize = 8192
it.idleTimeout = Duration.ofSeconds(600)
}
return DefaultHandshakeHandler(strategy)
}
}
WebSocket 経由で STOMP を使用する場合は、STOMP WebSocket トランスポートプロパティも構成する必要があります。 |
許可されたオリジン
Spring Framework 4.1.5 以降、WebSocket および SockJS のデフォルトの動作では、同一生成元のリクエストのみを受け入れます。すべてまたは指定されたオリジンのリストを許可することもできます。このチェックは、主にブラウザークライアント用に設計されています。他の型のクライアントが Origin
ヘッダー値を変更することを妨げるものはありません(詳細については、RFC 6454: Web Origin のコンセプト [IETF] (英語) を参照してください)。
3 つの可能な動作は次のとおりです。
同一オリジンリクエストのみを許可(デフォルト): このモードでは、SockJS が有効になっている場合、リクエストのオリジンのチェックが許可されないため、Iframe HTTP レスポンスヘッダー
X-Frame-Options
がSAMEORIGIN
に設定され、JSONP トランスポートが無効になります。そのため、このモードが有効になっている場合、IE6 および IE7 はサポートされません。指定されたオリジンのリストを許可: 許可された各オリジンは
http://
またはhttps://
で始まる必要があります。このモードでは、SockJS を有効にすると、IFrame トランスポートが無効になります。そのため、このモードが有効になっている場合、IE6 から IE9 はサポートされません。すべてのオリジンを許可: このモードを有効にするには、許可された起点値として
*
を提供する必要があります。このモードでは、すべてのトランスポートが利用可能です。
次の例に示すように、WebSocket と SockJS が許可するオリジンを設定できます。
Java
Kotlin
XML
@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
@Configuration
@EnableWebSocket
class WebSocketConfiguration : WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("https://mydomain.com")
}
@Bean
fun myHandler(): WebSocketHandler {
return MyHandler()
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:handlers allowed-origins="https://mydomain.com">
<websocket:mapping path="/myHandler" handler="myHandler" />
</websocket:handlers>
<bean id="myHandler" class="org.springframework.docs.web.websocket.websocketserverhandler.MyHandler" />
</beans>