1. 序章

このチュートリアルでは、 Akka HTTP を使用してHTTPベースのAPIを構築するときに、応答本文をStringとして読み取る方法を説明します。 最初にAkkaHTTPの概要を説明し、次に応答の本文をStringとして読み取る2つの方法を見ていきます。厳密な要求とアンマーシャリングです。

2. Akka HTTP

Akka HTTPは、Akkaのアクターとストリームモジュールの上にクライアント/サーバーHTTPスタックを実装するために使用されるAkkaモジュールです。 HTTPエンドポイントを構築するためのDSL(ドメイン固有言語)と、 HTTP呼び出しを実行します。

Akka HTTPは、ストリーミングを念頭に置いて設計されました。 要求と応答の両方がサーバーを介してストリーミングされ、メモリ消費を削減し、バックプレッシャーを提供して、リモートクライアントがサーバーが消費できるよりも速くデータを生成しないようにします。

2.1. 依存関係

Akka HTTPはAkkaアクターとストリームに依存しているため、いくつかの依存関係をプロジェクトにインポートします。

val AkkaVersion = "2.6.8"
val AkkaHttpVersion = "10.2.7"
libraryDependencies ++= Seq(
    "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion,
    "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
    "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
)

2.2. シンプルなルート

このチュートリアルでは、ルーティングDSL を使用して単純なHTTPサーバーを構築し、応答をStringとして読み取る方法を説明します。

route は、特定のホストとポートにバインドした後に呼び出すことができる単なるエンドポイントです。

まず、実験するための簡単なルートを作成しましょう。

object SimpleRouter {
  val route: Route = path("hello") {
    get {
      complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Hello, world!"))
    }
  }
}

上記の例では、AkkaHTTPのDSLを使用してエンドポイントを定義しました。 スコープはオブジェクト SimpleRouter のスコープであるため、SimpleRouter.routeでルートを簡単に参照できます。パスは/helloであり、GETリクエストのみを受け入れます。最後に、 complete を使用して、送信する応答を定義します。

それでは、ルートをhttp:// localhost:8080で到達可能にする方法を見てみましょう。

object App {
  def main(args: Array[String]): Unit = {
    implicit val system: ActorSystem[Nothing] =
      ActorSystem(Behaviors.empty, "simple-system")
    implicit val executionContext: ExecutionContextExecutor =
      system.executionContext

    val binding = Http().newServerAt("localhost", 8080).bind(SimpleRouter.route)

    binding
      .flatMap(_.unbind())
      .onComplete(_ => system.terminate())
  }
}

例の核となるのは、バインディングに割り当てる値です。Http()。newServerAt( “localhost”、8080).bind(SimpleRouter.route)。ここで、ホストをlocalhostとして指定します。 および8080としてのポート。 また、この場合は、そのホストとポートSimpleRouter.routeで使用できるようにするルートをAkkaHTTPに通知します。

ただし、そのコード行をコンパイルするには、いくつかの暗黙的なパラメーターが必要です。特に、Akka HTTPはAkkaアクターに大きく依存しているため、ActorSystemが必要です。さらに型付きバージョンのAkkaを使用しているため、例では ActorSystem Nothingの型パラメーターが必要です。 これは、システムに動作がなく、実行コンテキストを提供するために必要なだけであることを意味します。 次にimplicitとして宣言するのは、バインディング[でflatMapおよびonCompleteを呼び出すことができるExecutionContextExecutorです。 X168X]将来

最後に、アプリケーションの終了時にサーバーのバインドを解除して、ポートを解放する必要があります。最後に、ActorSystemも終了します。 Future :: flatMap() Future :: onComplete()。を活用することで、非同期的にこれを行うことができます。

3. 文字列としての応答本文

単純なHTTPサーバーを起動したら、http:// localhost:8080 / helloに移動して、「Hello、world!」を表示できます。 サーバーと対話する方法と、応答をStringとして読み取る方法を見てみましょう。

3.1. 厳格なリクエスト

Akka HTTPのストリーミングの性質により、応答のコンテンツはライブラリ内のストリームとして扱われます。これを回避する方法を見てみましょう。

val responseFuture = 
  Http().singleRequest(HttpRequest(uri = "http://localhost:8080/hello"))
val timeout = 300.millis
val responseAsString = Await.result(
  responseFuture
    .flatMap { resp => resp.entity.toStrict(timeout)}
    .map { strictEntity => strictEntity.data.utf8String },
  timeout
)

assert(responseAsString == "Hello, world!")

まず、Http()。singleRequest()を介して単純なエンドポイントにHTTP呼び出しを行います。 これにより、Future[HttpResponse]が得られます。 実際のアプリケーションを作成している場合は、 未来次のような方法を使用する地図 flatMap 、 等々。 今のところ、タイムアウトが300ミリ秒の Await:result()を使用して、Futureが完了するのを待ちます。 それでも、responseFutureを操作して、応答のコンテンツを抽出できます。

次に、応答の本文( resp.entity )を抽出します。 ここでは、 HttpEntity :: toStrict()を使用して、AkkaHTTPのストリーミングの性質を回避する必要があります。 HttpEntity :: toStrict()には、別の暗黙的なパラメーターであるMaterializerのインスタンスが必要です。 Materializer は、ストリームを実際に「実行」し、出力として値を生成するコンポーネントです。 したがって、基本的に、応答の本文を含むストリームを排出し、すべてをメモリにロードします。これは非同期操作であるため、タイムアウトも指定する必要があります。

この例では、ActorSystemおよびExecutionContextExecutorの場合のように、Materializerのインスタンスを明示的に定義していません。 これは、MaterializerクラスがActorSystemからMaterializerへの暗黙の変換を提供するためです。したがって、ScalaはActorSystemからMaterializerを取得します。

応答本文がメモリにあると、データを抽出して文字列(strictEntity.data.utf8String)に変換できます。最初の部分strictEntity.data ByteString のインスタンス。これは、基本的にバイトのシーケンスです。

対応するFutureが完了すると、resposeAsStringにはUTF-8でエンコードされた応答の表現が含まれます。

3.2. アンマーシャリング

Akka HTTPは、 String Boolean Int 、およびその他すべての数値データ型を含む、多くの型の事前定義されたアンマーシャラーをすぐに提供します。

Stringにそれをどのように使用できるか見てみましょう。

val responseFuture = 
  Http().singleRequest(HttpRequest(uri = "http://localhost:8080/hello"))
val timeout = 300.millis
val responseAsString = Await.result(
  responseFuture.flatMap(resp => Unmarshal(resp.entity).to[String]),
  timeout
)

assert(responseAsString == "Hello, world!")

ここでも、最初に単純なエンドポイントに対してHTTP呼び出しを行います。

Akka HTTPのストリーミングの性質に関連する低レベルの詳細を処理する必要がないため、応答を読み取るためのコードが以前よりもはるかに単純になりました。を使用するだけです。 ] Unmarshal :: to()メソッドを使用して、 HttpEntity (つまり、 resp.entity )のインスタンスをStringに変換します。 Await :: result()を使用して、Futureが完了するのを待ちます。

実際のところ、2番目の例でのAkka Streamsへの唯一の参照は、 Unmarshal :: to()の呼び出しで暗黙のMaterializerが必要なことです。 ActorSystemからの暗黙の変換がそれを処理します。

4. 結論

この記事では、Akka HTTPを使用するときに、応答の本文をStringとして読み取る方法を説明しました。 最初に非常に単純なHTTPサーバーを作成し、それを使用して「厳密な」エンティティと非マーシャリングの2つの方法を比較しました。 特に、後者はAkka HTTPのストリーミングの性質に関する多くの低レベルの詳細を隠すため、続行するための推奨される方法です。

いつものように、コードはGitHubにあります。