ルーティングDSLの概要

ルーティングDSLの概要

Akka HTTP 低レベルサーバーサイドAPI は、アプリケーションが受信したHTTPリクエストを簡単にレスポンスにマッピングする為の Flow``若しくは ``Function レベルのインターフェースを提供しています。 ( Low-level server side example から抜粋。)

final Function<HttpRequest, HttpResponse> requestHandler =
  new Function<HttpRequest, HttpResponse>() {
    private final HttpResponse NOT_FOUND =
      HttpResponse.create()
        .withStatus(404)
        .withEntity("Unknown resource!");


    @Override
    public HttpResponse apply(HttpRequest request) throws Exception {
      Uri uri = request.getUri();
      if (request.method() == HttpMethods.GET) {
        if (uri.path().equals("/")) {
          return
            HttpResponse.create()
              .withEntity(ContentTypes.TEXT_HTML_UTF8,
                "<html><body>Hello world!</body></html>");
        } else if (uri.path().equals("/hello")) {
          String name = uri.query().get("name").orElse("Mister X");

          return
            HttpResponse.create()
              .withEntity("Hello " + name + "!");
        } else if (uri.path().equals("/ping")) {
          return HttpResponse.create().withEntity("PONG!");
        } else {
          return NOT_FOUND;
        }
      } else {
        return NOT_FOUND;
      }
    }
  };

受信した HttpRequest に対してパターンマッチを使った完璧なREST APIを完全に定義する事が可能ですが、このアプローチは大量の文法の"儀式"を必要とし、比較的大きなサービスにはいくらか扱いにくくなってしまいます。また、サービスの定義をあなたが好む DRY に保つのに役に立ちません。

Akka HTTPはその代わりとして、あなたのサービスの振る舞いを簡潔で読みやすく記述できる要素の構造( ディレクティブ と呼ばれています)として表現できる柔軟なDSLを提供しています。ディレクティブは トップレベルにおいて bind を呼ぶ事で直接供給される Flow 処理 (若しくは、非同期な処理関数)で構成されるルート構造体として組み上げられます。

構成可能な高レベルAPIによって書き直された完全な例を次に示します。

import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.http.javadsl.ConnectHttp;
import akka.http.javadsl.Http;
import akka.http.javadsl.ServerBinding;
import akka.http.javadsl.model.ContentTypes;
import akka.http.javadsl.model.HttpEntities;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
import akka.http.javadsl.server.AllDirectives;
import akka.http.javadsl.server.Route;
import akka.stream.ActorMaterializer;
import akka.stream.javadsl.Flow;

import java.io.IOException;
import java.util.concurrent.CompletionStage;

public class HighLevelServerExample extends AllDirectives {
  public static void main(String[] args) throws IOException {
    // boot up server using the route as defined below
    ActorSystem system = ActorSystem.create();

    // HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
    final HighLevelServerExample app = new HighLevelServerExample();

    final Http http = Http.get(system);
    final ActorMaterializer materializer = ActorMaterializer.create(system);

    final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = app.createRoute().flow(system, materializer);
    final CompletionStage<ServerBinding> binding = http.bindAndHandle(routeFlow, ConnectHttp.toHost("localhost", 8080), materializer);

    System.out.println("Type RETURN to exit");
    System.in.read();
    
    binding
      .thenCompose(ServerBinding::unbind)
      .thenAccept(unbound -> system.terminate());
  }

  public Route createRoute() {
    // This handler generates responses to `/hello?name=XXX` requests
    Route helloRoute =
      parameterOptional("name", optName -> {
        String name = optName.orElse("Mister X");
        return complete("Hello " + name + "!");
      });

    return
      // here the complete behavior for this server is defined

      // only handle GET requests
      get(() -> route(
        // matches the empty path
        pathSingleSlash(() ->
          // return a constant string with a certain content type
          complete(HttpEntities.create(ContentTypes.TEXT_HTML_UTF8, "<html><body>Hello world!</body></html>"))
        ),
        path("ping", () ->
          // return a simple `text/plain` response
          complete("PONG!")
        ),
        path("hello", () ->
          // uses the route defined above
          helloRoute
        )
      ));
  }
}

ルーティングDSLのコアは、一つのインポートで有効になります。

import akka.http.javadsl.server.Directives.*;

また、全てのディレクティブを一つのクラスにまとめてアクセスしやすくする akka.http.javadsl.server.AllDirectives を拡張することによって

extends AllDirectives

もちろん、必要なディレクティブのみ (例えば、WebSocketDirectives など) をインポートすることも可能です。

Contents