はじめに
zio-http
は、ZIOベースのHTTPライブラリです。
ルートにミドルウェア設定を適用するためには、@@
演算子を使います。
この記事では、以下のサンプルコードを使って、この@@
演算子の使い方を解説します。
サンプルコード
import zio._
import zio.http._
object Main extends ZIOAppDefault {
val app: Routes[Any, Response] = Routes(
Method.GET / "text" -> handler {
for {
_ <- ZIO.sleep(Duration.fromSeconds(1))
} yield Response.text("Hello World!")
}
)
val middlewares = Middleware.timeout(Duration.fromSeconds(2))
val run = Server.serve(app @@ middlewares).provide(Server.default)
}
コード解説
ルートの定義
まず、シンプルなルートを定義しています。このルートは、/text
へのGET
リクエストに対して1秒待ってから"Hello World!"というテキストを返します。
val app: Routes[Any, Response] = Routes(
Method.GET / "text" -> handler {
for {
_ <- ZIO.sleep(Duration.fromSeconds(1))
} yield Response.text("Hello World!")
}
)
ミドルウェアの定義
次に、タイムアウトのミドルウェアを定義しています。このミドルウェアは、リクエストが2秒以内に完了しない場合にタイムアウトエラーを返します。
val middlewares = Middleware.timeout(Duration.fromSeconds(2))
@@
演算子の使用
最後に、@@
演算子を使って、定義したルートにミドルウェアを適用しています。これにより、app
のすべてのルートがミドルウェアの影響を受けることになります。
val run = Server.serve(app @@ middlewares).provide(Server.default)
@@
演算子の詳細
@@
演算子は一見すると何をしているのか分かりにくいかもしれませんが、実態はシンプルで、
Routes
クラスの@@
メソッドを見てみると、ミドルウェアのapply
メソッドを呼び出しているだけです。
Routes
の実装
Routes
クラスは、複数のルートを含むデータ構造です。@@
演算子は、ミドルウェアをルートに適用するために使われます。
// ZIOの実装から抜粋
final case class Routes[-Env, +Err](routes: Chunk[zio.http.Route[Env, Err]]) { self =>
private var _tree: Routes.Tree[_] = null.asInstanceOf[Routes.Tree[_]]
def @@[Env1 <: Env](aspect: Middleware[Env1]): Routes[Env1, Err] =
aspect(self)
}
Middleware
の実装
Middleware
は、リクエストを処理する前後に追加のロジックを挟むための抽象クラスです。例えば、timeout
は、指定した時間内にリクエストが完了しなければタイムアウトエラーを返します。
// ZIOの実装から抜粋
object Middleware extends HandlerAspects {
def timeout(duration: Duration)(implicit trace: Trace): Middleware[Any] =
new Middleware[Any] {
def apply[Env1 <: Any, Err](routes: Routes[Env1, Err]): Routes[Env1, Err] =
routes.transform[Env1] { handler =>
handler.timeoutFail(Response(status = Status.RequestTimeout))(duration)
}
}
}
@@
演算子の背後では、routes.transform
メソッドが呼び出され、各ハンドラにミドルウェアが適用されているだけです。
このシンプルな仕組みにより、@@
演算子は直感的かつ柔軟にミドルウェアを適用することができます。
まとめ
zio-http
の@@
演算子を使うことで、ルートに対して簡単にミドルウェアを適用できます。
内部的には、単にroutes.transform
を呼び出しているだけでした。