Spark Javaフレームワークを使ったAPIの構築

Spark Java Frameworkを使用したAPIの構築

1. 前書き

この記事では、Spark frameworkについて簡単に紹介します。 Sparkフレームワークは、Ruby用のSinatraフレームワークに触発された迅速な開発Webフレームワークであり、Java 8 Lambda Expression哲学に基づいて構築されており、他のJavaフレームワークで記述されたほとんどのアプリケーションよりも冗長ではありません。

JavaでWebAPIまたはマイクロサービスを開発するときにNode.jsのようなエクスペリエンスが必要な場合に適しています。 Sparkを使用すると、10行未満のコードでJSONを提供するREST APIを準備できます。

「Hello World」の例から始めて、簡単なREST APIを作成します。

2. Mavenの依存関係

2.1. Sparkフレームワーク

次のMaven依存関係をpom.xmlに含めます。


    com.sparkjava
    spark-core
    2.5.4

Sparkの最新バージョンはMaven Centralにあります。

2.2. Gsonライブラリ

例のさまざまな場所で、JSON操作にGsonライブラリを使用します。 プロジェクトにGsonを含めるには、次の依存関係をpom.xmlに含めます。


    com.google.code.gson
    gson
    2.8.0

Gsonの最新バージョンはMaven Centralにあります。

3. SparkFramework入門

Sparkアプリケーションの基本的な構成要素を見て、簡単なWebサービスを示しましょう。

3.1. ルート

Spark JavaのWebサービスは、ルートとそのハンドラーに基づいて構築されます。 ルートはSparkの重要な要素です。 documentationに従って、各ルートは3つの単純な部分(verbpath、およびcallback)で構成されます。

  1. verbは、HTTPメソッドに対応するメソッドです。 動詞メソッドには、get, post, put, delete, head, trace, connect,およびoptionsが含まれます。

  2. path(ルートパターンとも呼ばれます)は、ルートがリッスンするURIを決定し、

  3. callbackは、対応するHTTP要求への応答を生成して返すために、特定の動詞とパスに対して呼び出されるハンドラー関数です。 コールバックは、リクエストオブジェクトとレスポンスオブジェクトを引数として受け取ります

ここでは、get動詞を使用するルートの基本構造を示します。

get("/your-route-path/", (request, response) -> {
    // your callback code
});

3.2. Hello World API

GETリクエスト用に2つのルートがあり、応答として「Hello」メッセージを返す単純なWebサービスを作成しましょう。 これらのルートは、クラスspark.Sparkからの静的インポートであるgetメソッドを使用します。

import static spark.Spark.*;

public class HelloWorldService {
    public static void main(String[] args) {

        get("/hello", (req, res)->"Hello, world");

        get("/hello/:name", (req,res)->{
            return "Hello, "+ req.params(":name");
        });
    }
}

getメソッドの最初の引数は、ルートのパスです。 最初のルートには、単一のURI(“/hello”)のみを表す静的パスが含まれています。

2番目のルートのパス(“/hello/:name”)には、“name”パラメータのプレースホルダーが含まれています。これは、パラメータの前にコロン( ":")を付けることで示されます。 このルートは、“/hello/Joe”“/hello/Mary”などのURIへのGET要求に応答して呼び出されます。

getメソッドの2番目の引数は、このフレームワークに関数型プログラミングフレーバーを与えるlambda expressionです。

ラムダ式には、引数として要求と応答があり、応答を返すのに役立ちます。 このチュートリアルの後半で説明するように、REST APIルートのラムダ式にコントローラーロジックを配置します。

3.3. Hello WorldAPIのテスト

クラスHelloWorldServiceを通常のJavaクラスとして実行した後、上記のgetメソッドで定義されたルートを使用して、デフォルトのポート4567でサービスにアクセスできるようになります。

最初のルートのリクエストとレスポンスを見てみましょう。

要求:

GET http://localhost:4567/hello

応答:

Hello, world

パスにnameパラメータを渡して、2番目のルートをテストしてみましょう。

要求:

GET http://localhost:4567/hello/example

応答:

Hello, example

URI内のテキスト“example”の配置が、ルートパターン“/hello/:name”と一致するためにどのように使用されたかを確認します。これにより、2番目のルートのコールバックハンドラー関数が呼び出されます。

4. RESTfulサービスの設計

このセクションでは、次のUserエンティティ用の単純なRESTWebサービスを設計します。

public class User {
    private String id;
    private String firstName;
    private String lastName;
    private String email;

    // constructors, getters and setters
}

4.1. ルート

APIを構成するルートをリストしてみましょう。

  • GET / users –すべてのユーザーのリストを取得します

  • GET / users /:id —指定したIDのユーザーを取得します

  • POST / users /:id –ユーザーを追加します

  • PUT / users /:id –特定のユーザーを編集します

  • オプション/ users /:id —指定されたIDのユーザーが存在するかどうかを確認します

  • DELETE / users /:id —特定のユーザーを削除します

4.2. ユーザーサービス

以下は、UserエンティティのCRUD操作を宣言するUserServiceインターフェイスです。

public interface UserService {

    public void addUser (User user);

    public Collection getUsers ();
    public User getUser (String id);

    public User editUser (User user)
      throws UserException;

    public void deleteUser (String id);

    public boolean userExist (String id);
}

デモンストレーションの目的で、永続性をシミュレートするために、GitHubコードでこのUserServiceインターフェイスのMap実装を提供します。 You can supply your own implementation with the database and persistence layer of your choice.

4.3. JSON応答構造

以下は、RESTサービスで使用される応答のJSON構造です。

{
    status: 
    message: 
    data: 
}

statusフィールド値は、SUCCESSまたはERRORのいずれかです。 dataフィールドには、UserUsersのコレクションなどの戻りデータのJSON表現が含まれます。

返されるデータがない場合、またはstatusERRORの場合は、messageフィールドにデータを入力して、エラーまたは返されるデータの欠如の理由を伝えます。

Javaクラスを使用して上記のJSON構造を表現しましょう。

public class StandardResponse {

    private StatusResponse status;
    private String message;
    private JsonElement data;

    public StandardResponse(StatusResponse status) {
        // ...
    }
    public StandardResponse(StatusResponse status, String message) {
        // ...
    }
    public StandardResponse(StatusResponse status, JsonElement data) {
        // ...
    }

    // getters and setters
}

ここで、StatusResponseは次のように定義されたenumです。

public enum StatusResponse {
    SUCCESS ("Success"),
    ERROR ("Error");

    private String status;
    // constructors, getters
}

5. RESTfulサービスの実装

それでは、RESTAPIのルートとハンドラーを実装しましょう。

5.1. コントローラの作成

次のJavaクラスには、動詞とパス、および各ルートのハンドラーのアウトラインを含む、APIのルートが含まれています。

public class SparkRestExample {
    public static void main(String[] args) {
        post("/users", (request, response) -> {
            //...
        });
        get("/users", (request, response) -> {
            //...
        });
        get("/users/:id", (request, response) -> {
            //...
        });
        put("/users/:id", (request, response) -> {
            //...
        });
        delete("/users/:id", (request, response) -> {
            //...
        });
        options("/users/:id", (request, response) -> {
            //...
        });
    }
}

次のサブセクションで、各ルートハンドラの完全な実装を示します。

5.2. ユーザーを追加する

以下は、Userを追加するpostメソッド応答ハンドラーです。

post("/users", (request, response) -> {
    response.type("application/json");
    User user = new Gson().fromJson(request.body(), User.class);
    userService.addUser(user);

    return new Gson()
      .toJson(new StandardResponse(StatusResponse.SUCCESS));
});

Note:この例では、UserオブジェクトのJSON表現がPOSTリクエストの生の本文として渡されます。

ルートをテストしてみましょう。

要求:

POST http://localhost:4567/users
{
    "id": "1012",
    "email": "[email protected]",
    "firstName": "Mac",
    "lastName": "Mason1"
}

応答:

{
    "status":"SUCCESS"
}

5.3. すべてのユーザーを取得

以下は、UserServiceからすべてのユーザーを返すgetメソッド応答ハンドラーです。

get("/users", (request, response) -> {
    response.type("application/json");
    return new Gson().toJson(
      new StandardResponse(StatusResponse.SUCCESS,new Gson()
        .toJsonTree(userService.getUsers())));
});

それでは、ルートをテストしましょう。

要求:

GET http://localhost:4567/users

応答:

{
    "status":"SUCCESS",
    "data":[
        {
            "id":"1014",
            "firstName":"John",
            "lastName":"Miller",
            "email":"[email protected]"
        },
        {
            "id":"1012",
            "firstName":"Mac",
            "lastName":"Mason1",
            "email":"[email protected]"
        }
    ]
}

5.4. IDでユーザーを取得

以下は、指定されたidUserを返すgetメソッド応答ハンドラーです。

get("/users/:id", (request, response) -> {
    response.type("application/json");
    return new Gson().toJson(
      new StandardResponse(StatusResponse.SUCCESS,new Gson()
        .toJsonTree(userService.getUser(request.params(":id")))));
});

それでは、ルートをテストしましょう。

要求:

GET http://localhost:4567/users/1012

応答:

{
    "status":"SUCCESS",
    "data":{
        "id":"1012",
        "firstName":"Mac",
        "lastName":"Mason1",
        "email":"[email protected]"
    }
}

5.5. ユーザーを編集する

以下は、putメソッド応答ハンドラーです。これは、ルートパターンでidが指定されているユーザーを編集します。

put("/users/:id", (request, response) -> {
    response.type("application/json");
    User toEdit = new Gson().fromJson(request.body(), User.class);
    User editedUser = userService.editUser(toEdit);

    if (editedUser != null) {
        return new Gson().toJson(
          new StandardResponse(StatusResponse.SUCCESS,new Gson()
            .toJsonTree(editedUser)));
    } else {
        return new Gson().toJson(
          new StandardResponse(StatusResponse.ERROR,new Gson()
            .toJson("User not found or error in edit")));
    }
});

Note:この例では、データは、プロパティ名が編集対象のUserオブジェクトのフィールドと一致するJSONオブジェクトとしてPOSTリクエストの生の本文で渡されます。

ルートをテストしてみましょう。

要求:

PUT http://localhost:4567/users/1012
{
    "lastName": "Mason"
}

応答:

{
    "status":"SUCCESS",
    "data":{
        "id":"1012",
        "firstName":"Mac",
        "lastName":"Mason",
        "email":"[email protected]"
    }
}

5.6. ユーザーを削除する

以下は、deleteメソッド応答ハンドラーです。これにより、指定されたidUserが削除されます。

delete("/users/:id", (request, response) -> {
    response.type("application/json");
    userService.deleteUser(request.params(":id"));
    return new Gson().toJson(
      new StandardResponse(StatusResponse.SUCCESS, "user deleted"));
});

それでは、ルートをテストしてみましょう。

要求:

DELETE http://localhost:4567/users/1012

応答:

{
    "status":"SUCCESS",
    "message":"user deleted"
}

5.7. ユーザーが存在するかどうかを確認します

optionsメソッドは、条件付きチェックに適しています。 以下は、指定されたidを持つUserが存在するかどうかをチェックするoptionsメソッド応答ハンドラーです。

options("/users/:id", (request, response) -> {
    response.type("application/json");
    return new Gson().toJson(
      new StandardResponse(StatusResponse.SUCCESS,
        (userService.userExist(
          request.params(":id"))) ? "User exists" : "User does not exists" ));
});

それでは、ルートをテストしましょう。

要求:

OPTIONS http://localhost:4567/users/1012

応答:

{
    "status":"SUCCESS",
    "message":"User exists"
}

6. 結論

この記事では、迅速なWeb開発のためのSparkフレームワークを簡単に紹介しました。

このフレームワークは、主にJavaでマイクロサービスを生成するために推進されています。 JVMライブラリ上に構築されたライブラリを活用したいJavaの知識を持つNode.jsの開発者は、このフレームワークを使用することに慣れているはずです。

そしていつものように、このチュートリアルのすべてのソースはGithub projectにあります。