AxumでWebSocketを使うときにOpenAPI Generatorのカスタムテンプレートを使用してハンドラーの生成をする知見
最近OpenAPI Generatorにrust-axum
が追加されているのを発見し、以前開発していたWebSocketサーバーに適用出来ないか試してみました。
library
axumとOpenAPI Generatorのバージョンは以下の指定をしています。
[dependencies.axum]
version = '0.7.5'
features = [
'ws',
]
OpenAPI Generator: v7.5.0
Generateに使うYaml
生成用のYamlは以下でPath Parameterにchannel_id
を入れています。
また、特殊な記述としてx-websocket
を入れています。これは後で説明するカスタムテンプレートを使う上で必要になる記述です。
openapi: 3.1.0
info:
title: WebSocket
version: 0.0.1
servers:
- url: http://localhost:8088
paths:
/websocket/{channel_id}:
get:
description: WebSocketに接続する
operationId: connectWebSocket
x-websocket: true
servers:
- url: ws://localhost:8088
parameters:
- in: path
name: channel_id
required: true
schema:
type: string
- in: header
name: Connection
required: true
schema:
type: string
- in: header
name: Upgrade
required: true
schema:
type: string
- in: header
name: Sec-WebSocket-Key
required: true
schema:
type: string
- in: header
name: Sec-WebSocket-Version
required: true
schema:
type: string
responses:
"101":
headers:
Connection:
required: true
schema:
type: string
Upgrade:
required: true
schema:
type: string
Sec-WebSocket-Accept:
required: true
schema:
type: string
description: 101 response
"default":
content:
text/plain:
schema:
type: string
description: Error response
tags:
- websocket
カスタムテンプレートの作成
デフォルトのテンプレートだと生成されるコードが不十分なのでカスタムテンプレートを作ります。
x-websocket
がyamlにある場合ws::WebSocketUpgrade
というExtractorがhandlerの引数に追加されるようになります。
lib.mustache
--- lib.mustache.org 2024-06-18 21:44:16
+++ lib.mustache 2024-06-18 20:40:17
@@ -88,6 +88,9 @@
{{#x-consumes-multipart-related}}
body: axum::body::Body,
{{/x-consumes-multipart-related}}
+ {{#x-websocket}}
+ web_socket: ws::WebSocketUpgrade
+ {{/x-websocket}}
) -> Result<{{{operationId}}}Response, String>;
{{/vendorExtensions}}
server-operation.mustache
--- server-operation.mustache.org 2024-06-18 21:41:21
+++ server-operation.mustache 2024-06-18 20:40:17
@@ -15,6 +15,9 @@
{{/queryParams.size}}
State(api_impl): State<I>,
{{#vendorExtensions}}
+{{#x-websocket}}
+ web_socket: ws::WebSocketUpgrade,
+{{/x-websocket}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParam}}
@@ -164,6 +167,9 @@
query_params,
{{/queryParams.size}}
{{#vendorExtensions}}
+ {{#x-websocket}}
+ web_socket,
+ {{/x-websocket}}
{{^x-consumes-multipart-related}}
{{^x-consumes-multipart}}
{{#bodyParams}}
コード生成と統合
Apiというトレイトが生成されるのでそれを実装していく形式となります。
あとはtodo()
の部分にWebSocketのメッセージのstreamを扱う処理を書くことになります。
struct ServerImpl {
// database: sea_orm::DbConn,
}
#[allow(unused_variables)]
#[async_trait]
impl websocket::Api for ServerImpl {
#[doc = r" ConnectWebSocket - GET /websocket/{channel_id}"]
#[must_use]
#[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
fn connect_web_socket<'life0, 'async_trait>(
&'life0 self,
method: Method,
host: Host,
cookies: CookieJar,
header_params: models::ConnectWebSocketHeaderParams,
path_params: models::ConnectWebSocketPathParams,
web_socket: ws::WebSocketUpgrade,
) -> ::core::pin::Pin<
Box<
dyn ::core::future::Future<Output = Result<ConnectWebSocketResponse, String>>
+ ::core::marker::Send
+ 'async_trait,
>,
>
where
'life0: 'async_trait,
Self: 'async_trait,
{
todo!()
}
}
まとめ
OpenAPI Generatorでrust-axum
を使ってコードを生成してみましたがWebSocketなど追加でExtractorが必要になってくる場合はテンプレートを書き換える必要が頻繁に出てくるといった印象でした。
また、最終的に生成されるコードとしてRouterまで生成されるので.layer
等を好きなように挿入出来ないです。なので生成されたものすべてを使うかはプロジェクトによって精査は必要かと思います。
シンプルなサーバーを生成する上ではとても便利だと思うのでより良い使い方を見つけていけたらと思いました。
Discussion