1. 序章

http4sは、Scala 用の型付き機能的なHTTPライブラリであり、ストリーミングをサポートしています。 このチュートリアルでは、基本的なトピックに焦点を当てて、http4の概要を説明します。 特に、http4s DSLを使用して単純なHTTPサーバーを作成する方法と、http4sクライアントを使用してHTTP呼び出しを行う方法について説明します。

2. http4sの概要

http4sは、 cats (いくつかのコアデータ型を共有する)、 cats-effect (効果的なコードを記述するため)、などの他の多くのライブラリに基づいて構築されています。 ] FS2 (ストリーミングサポート用)。 build.sbtに依存関係を追加することから始めましょう:

val http4sVersion = "0.23.10"

libraryDependencies ++= Seq(
  "org.http4s" %% "http4s-dsl" % http4sVersion,
  "org.http4s" %% "http4s-blaze-server" % http4sVersion,
  "org.http4s" %% "http4s-blaze-client" % http4sVersion
)

3. http4sエンドポイント定義用のDSL

http4sは、リクエストとレスポンスの概念に基づいています。ライブラリは、サーバーを、リクエストレスポンスに変換する関数を含むルートのセットとして定義します。タイプHttpRoutes[F]を使用します。 HttpRoutes [F] は、 Kleisli [OptionT [F、*]、Request、 Response] のエイリアスであり、かなり複雑なタイプです。 簡単に言うと、 Kleisli [OptionT [F、*]、Request、Response]はRequest => F [Option [Response]]のラッパーを表し、Fは効果的な操作を表します。を使用しますすべてのリクエストレスポンスにつながるわけではないため、]オプション F が必要なのは、応答の生成にはデータベースへのアクセスや外部サービスの呼び出しなどの副作用が伴う可能性があるためです。 したがって、 HttpRoutes [F] は、RequestからオプションのResponseまでの関数を表し、副作用が含まれる可能性があります。 http4s DSLは、リクエストのパターンマッチングによってルートを定義するために使用されます。パラメータとして渡された文字列の長さを返す単純なエンドポイントを定義する方法を見てみましょう。

implicit val runtime: IORuntime = cats.effect.unsafe.IORuntime.global

val route: HttpRoutes[IO] = HttpRoutes.of[IO] {
  case GET -> Root / "length" / str => Ok(str.length.toString)
}

上記の例では、エンドポイントの長さ/ {str}に応答する単一のルートを定義しています。ここで、{str}はパスパラメータを表します。 http4s DSLを使用して、最初にGETに応答することを指定します。 リクエスト、次にエンドポイント、そして動作。 Root は、サーバーによって構成されたルートパスを示します。 たとえば、 / をベースパスとして使用するようにサーバーを構成すると、エンドポイントは /lengthで応答します。 一方、サーバーがベースパスとして / api を使用している場合、 / api /lengthでエンドポイントに到達できます。 パスパラメータは、http4sではデフォルトでStringにマップされます。 したがって、strは自動的にStringを型として想定します。 ルートを定義するには、IORuntimeによって提供されるランタイムも定義する必要があります。 同様に、効果的な操作としてIOを使用します。 これは、ルートが副作用として値(応答)を出力することを意味します。

4. サーバーの実行

ルートを定義した後、エンドポイントを公開して呼び出すことができるようにサーバーを設定できます。http4sは複数のサーバーバックグラウンドをサポートしています。 この例では、ネイティブサーバーであるblazeを使用します。 簡単なサーバーをセットアップしましょう:

object SimpleServer extends IOApp {
  val route: HttpRoutes[IO] = HttpRoutes.of[IO] {
    case GET -> Root / "length" / str => Ok(str.length.toString)
  }

  val app: Kleisli[IO, Request[IO], Response[IO]] = Router(
    "/" -> route
  ).orNotFound

  override def run(args: List[String]): IO[ExitCode] =
    BlazeServerBuilder[IO]
      .bindHttp(8080, "localhost")
      .withHttpApp(app)
      .resource
      .useForever
      .as(ExitCode.Success)
}

この場合、 app を作成するときに、ルートをベースエンドポイント「/」にバインドしました。 これにより、 /lengthエンドポイントへのルートが公開されます。 また、 .orNotFound を呼び出すことで、ユーザーから送信されたリクエストが定義したエンドポイントと一致しない場合は404応答が返される必要があるとも言います。 サーバーを実行するための最初のステップは、IOAppを拡張することです。 これにより、以前のようにインポートしなくてもIOランタイムが得られます。 また、サーバーの起動と終了も処理します。 このように、サーバーにバインドされたポートは、アプリケーションが閉じられるとすぐに解放されます。 IOAppは、メインメソッドを定義し、runメソッドでサーバーの動作を指定できるようにします。ここでは、BlazeServerBuilderを作成しています。 次に、http4sに、サーバーをバインドするホスト名とポート、それぞれlocalhost8080を指定します。 次に、appで定義される「バックエンド」を選択します。 サーバーを起動および終了するためにhttp4sが次の手順を実行する必要があります。BlazeServerBuilder [IO].resourceは実際のサーバーを返します。 サーバーの起動は副作用であるため、ここでhttp4の効果的な性質が作用します。 リソースへの単純な呼び出しでは、サーバーは割り当てられません。 そのためには、useForeverを呼び出す必要があります。これにより、ポート8080がローカルホスト上のアプリケーションにバインドされます。 useForever アプリケーションを明示的に閉じるまでサーバーを実行するようにhttp4sに指示します。 最後に、アプリケーションの終了コードを指定します。

5. http4sHTTPクライアント

単純なHTTPサーバーを作成したら、それに対してHTTP呼び出しを行う方法を見てみましょう。

import cats.effect.unsafe.implicits.global

object SimpleClient extends IOApp {
  def callEffect(client: Client[IO], str: String): IO[String] =
    client.expect[String](uri"http://localhost:8080/length/" / str)

  override def run(args: List[String]): IO[ExitCode] =
    BlazeClientBuilder[IO].resource
      .use { client =>
        println(callEffect(client, "Baeldung").unsafeRunSync())
        IO.unit
      }
      .as(ExitCode.Success)
}

以前と同様に、 IOApp を拡張し、blazeを背景として使用し、BlazeClientBuilderを作成します。 resource を取得した後、 use を呼び出します。これにより、実際のクライアントが割り当てられ、ラムダ関数に提供されます。 エフェクトが完了するとすぐにクライアントが解放されます。 この場合、IO.unitを呼び出すことによってこれを行います。 callEffect ここで、上記で定義した単純なサーバーについて説明し、HTTP呼び出しを行います。 use によって提供されるHTTPクライアントと、HTTP呼び出しに引数として渡されるStringを入力します。 callEffectでは、Uriオブジェクトを作成します。この場合、専用のDSLも使用しており、strをパスパラメーターとして渡すことができます。 次に、クライアントでexpectを呼び出して、GETリクエストを実行します。応答のタイプは String になりますが、[ X191X]マップ。 たとえば、応答を Int に変換する場合は、 client.expect [String](uri” http:// localhost:8080 / length /” / str).mapを呼び出すことができます。 (_.toInt) callEffect は、タイプStringIO効果を返します。 実際の応答を取得するには、暗黙のランタイムが必要なunsafeRunSync()を介してこのエフェクトを実行する必要があります。この場合、cats.effect.unsafe.implicitsをインポートします。 global。unsafeRunSyncは、カプセル化された効果を不純な副作用として実行することによって結果を生成します。不純とは、このコンテキストでは、値の準備ができるまで呼び出しが現在のスレッドをブロックすることを意味します。 さらに、この方法でエフェクトを実行すると、例外がスローされる場合があります。 理想的には、このメソッドはプログラムの最後で1回呼び出されます。

6. 結論

この記事では、非常に複雑なライブラリであるhttp4sの非常に簡単な紹介を見ました。 DSLを介したエンドポイントの定義と、HTTP呼び出しを記述および実行する方法を確認しました。 いつものように、コードはGitHubにあります。