JavaのPlayFrameworkを使用したRESTAPI
1. 概要
このチュートリアルの目的は、Play Frameworkを探索し、Javaを使用してPlayFrameworkでRESTサービスを構築する方法を学ぶことです。
学生のレコードを作成、取得、更新、削除するためのRESTAPIをまとめます。
このようなアプリケーションでは、通常、学生の記録を保存するためのデータベースがあります。 Play Frameworkには、Hibernateおよびその他の永続化フレームワークを使用したJPAのサポートに加えて、組み込みのH2データベースがあります。
ただし、物事をシンプルに保ち、最も重要なことに焦点を当てるために、シンプルなマップを使用して、一意のIDを持つ学生オブジェクトを保存します。
2. 新しいアプリケーションを作成する
Play Frameworkの概要で説明されているようにPlayFrameworkをインストールすると、アプリケーションを作成する準備が整います。
sbt コマンドを使用して、 play- java-seedを使用してstudent-apiという新しいアプリケーションを作成しましょう。
sbt new playframework/play-java-seed.g8
3. モデル
アプリケーションのスキャフォールディングを配置したら、 student-api / app / models に移動して、学生情報を処理するためのJavaBeanを作成しましょう。
public class Student {
private String firstName;
private String lastName;
private int age;
private int id;
// standard constructors, getters and setters
}
次に、学生データ用の HashMap – に裏打ちされた、CRUD操作を実行するためのヘルパーメソッドを備えた単純なデータストアを作成します。
public class StudentStore {
private Map<Integer, Student> students = new HashMap<>();
public Optional<Student> addStudent(Student student) {
int id = students.size();
student.setId(id);
students.put(id, student);
return Optional.ofNullable(student);
}
public Optional<Student> getStudent(int id) {
return Optional.ofNullable(students.get(id));
}
public Set<Student> getAllStudents() {
return new HashSet<>(students.values());
}
public Optional<Student> updateStudent(Student student) {
int id = student.getId();
if (students.containsKey(id)) {
students.put(id, student);
return Optional.ofNullable(student);
}
return null;
}
public boolean deleteStudent(int id) {
return students.remove(id) != null;
}
}
4. コントローラー
student-api / app / controllers に移動して、StudentController。javaという新しいコントローラーを作成しましょう。 コードを段階的に進めていきます。
まず、 HttpExecutionContextを構成する必要があります。非同期の非ブロッキングコードを使用してアクションを実装します。 これは、アクションメソッドが返されることを意味します CompletionStage
Play Frameworkコントローラーで非同期プログラミングを処理する場合の注意点が1つだけあります。それは、HttpExecutionContextを提供する必要があることです。HTTP実行コンテキストを提供しないと、悪名高いエラーが発生します。アクションメソッドを呼び出すときに、「ここから利用できるHTTPコンテキストはありません」。
それを注入しましょう:
private HttpExecutionContext ec;
private StudentStore studentStore;
@Inject
public StudentController(HttpExecutionContext ec, StudentStore studentStore) {
this.studentStore = studentStore;
this.ec = ec;
}
StudentStore も追加し、@Injectアノテーションを使用してコントローラーのコンストラクターに両方のフィールドを挿入したことに注意してください。 これで、アクションメソッドの実装を進めることができます。
Playにはデータ処理を可能にするためにJacksonが付属していることに注意してください–外部の依存関係なしに必要なJacksonクラスをインポートできます。
繰り返し操作を実行するユーティリティクラスを定義しましょう。 この場合、HTTP応答を作成します。
それでは、 student-api / app / utils パッケージを作成し、それにUtil。javaを追加しましょう。
public class Util {
public static ObjectNode createResponse(Object response, boolean ok) {
ObjectNode result = Json.newObject();
result.put("isSuccessful", ok);
if (response instanceof String) {
result.put("body", (String) response);
} else {
result.putPOJO("body", response);
}
return result;
}
}
このメソッドを使用して、ブール値のisSuccessfulキーと応答本文を使用して標準のJSON応答を作成します。
これで、コントローラークラスのアクションをステップスルーできます。
4.1. createアクション
POST アクションとしてマップされたこのメソッドは、Studentオブジェクトの作成を処理します。
public CompletionStage<Result> create(Http.Request request) {
JsonNode json = request.body().asJson();
return supplyAsync(() -> {
if (json == null) {
return badRequest(Util.createResponse("Expecting Json data", false));
}
Optional<Student> studentOptional = studentStore.addStudent(Json.fromJson(json, Student.class));
return studentOptional.map(student -> {
JsonNode jsonObject = Json.toJson(student);
return created(Util.createResponse(jsonObject, true));
}).orElse(internalServerError(Util.createResponse("Could not create data.", false)));
}, ec.current());
}
挿入されたHttp.Requestクラスからの呼び出しを使用して、リクエストの本文をJacksonのJsonNodeクラスに取り込みます。 本文がnullの場合、ユーティリティメソッドを使用して応答を作成する方法に注目してください。
また、 CompletionStage
ステータスを示すbooleanフラグとともに、任意のStringまたはJsonNodeを渡すことができます。
また、 Json.fromJson()を使用して、着信JSONオブジェクトを Student オブジェクトに変換し、応答のためにJSONに戻す方法にも注目してください。
最後に、これまで使用していた ok()の代わりに、play.mvc.resultsパッケージのcreatedヘルパーメソッドを使用しています。 アイデアは、特定のコンテキスト内で実行されているアクションに正しいHTTPステータスを与えるメソッドを使用することです。 たとえば、HTTP OK200ステータスの場合はok()、HTTP CREATED201が上記の結果ステータスの場合はcreated()です。 この概念は、残りのアクション全体で発生します。
4.2. 更新アクション
PUTリクエストをhttp:// localhost:9000 / にヒットすると、StudentController。 update メソッドがヒットし、 StudentStoreのupdateStudentメソッド:
public CompletionStage<Result> update(Http.Request request) {
JsonNode json = request.body().asJson();
return supplyAsync(() -> {
if (json == null) {
return badRequest(Util.createResponse("Expecting Json data", false));
}
Optional<Student> studentOptional = studentStore.updateStudent(Json.fromJson(json, Student.class));
return studentOptional.map(student -> {
if (student == null) {
return notFound(Util.createResponse("Student not found", false));
}
JsonNode jsonObject = Json.toJson(student);
return ok(Util.createResponse(jsonObject, true));
}).orElse(internalServerError(Util.createResponse("Could not create data.", false)));
}, ec.current());
}
4.3. 取得アクション
学生を取得するには、 GETリクエストのパスパラメータとして学生のIDをhttp:// localhost:9000 /:idに渡します。 これにより、取得アクションが実行されます。
public CompletionStage<Result> retrieve(int id) {
return supplyAsync(() -> {
final Optional<Student> studentOptional = studentStore.getStudent(id);
return studentOptional.map(student -> {
JsonNode jsonObjects = Json.toJson(student);
return ok(Util.createResponse(jsonObjects, true));
}).orElse(notFound(Util.createResponse("Student with id:" + id + " not found", false)));
}, ec.current());
}
4.4. 削除アクション
delete アクションは、 http:// localhost:9000 /:idにマップされます。 削除するレコードを識別するために、idを提供します。
public CompletionStage<Result> delete(int id) {
return supplyAsync(() -> {
boolean status = studentStore.deleteStudent(id);
if (!status) {
return notFound(Util.createResponse("Student with id:" + id + " not found", false));
}
return ok(Util.createResponse("Student with id:" + id + " deleted", true));
}, ec.current());
}
4.5. listStudentsアクション
最後に、 listStudents アクションは、これまでに保存されたすべての学生のリストを返します。 GETリクエストとしてhttp:// localhost:9000/にマッピングされます。
public CompletionStage<Result> listStudents() {
return supplyAsync(() -> {
Set<Student> result = studentStore.getAllStudents();
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonData = mapper.convertValue(result, JsonNode.class);
return ok(Util.createResponse(jsonData, true));
}, ec.current());
}
5. マッピング
コントローラアクションを設定したら、ファイル student-api / conf / rules を開き、次のルートを追加することで、それらをマッピングできます。
GET / controllers.StudentController.listStudents()
GET /:id controllers.StudentController.retrieve(id:Int)
POST / controllers.StudentController.create(request: Request)
PUT / controllers.StudentController.update(request: Request)
DELETE /:id controllers.StudentController.delete(id:Int)
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
静的リソースをダウンロードするには、/assetsエンドポイントが常に存在している必要があります。
この後、 StudentAPIの構築が完了しました。
ルートマッピングの定義の詳細については、 RoutinginPlayアプリケーションチュートリアルにアクセスしてください。
6. テスト
これで、 http:// localhost:9000 / にリクエストを送信し、適切なコンテキストを追加することで、APIでテストを実行できます。 ブラウザからベースパスを実行すると、次のように出力されます。
{
"isSuccessful":true,
"body":[]
}
ご覧のとおり、まだレコードを追加していないため、本文は空です。 curl を使用して、いくつかのテストを実行してみましょう(または、PostmanのようなRESTクライアントを使用することもできます)。
ターミナルウィンドウを開いて、curlコマンドを実行して学生を追加しましょう。
curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"John","lastName":"Baeldung","age": 18}' \
http://localhost:9000/
これにより、新しく作成された学生が返されます。
{
"isSuccessful":true,
"body":{
"firstName":"John",
"lastName":"Baeldung",
"age":18,
"id":0
}
}
上記のテストを実行した後、ブラウザから http:// localhost:9000 をロードすると、次のようになります。
{
"isSuccessful":true,
"body":[
{
"firstName":"John",
"lastName":"Baeldung",
"age":18,
"id":0
}
]
}
id 属性は、新しいレコードを追加するたびにインクリメントされます。
レコードを削除するには、DELETEリクエストを送信します。
curl -X DELETE http://localhost:9000/0
{
"isSuccessful":true,
"body":"Student with id:0 deleted"
}
上記のテストでは、最初のテストで作成したレコードを削除します。次に、更新メソッドをテストできるように、再度作成してみましょう。
curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"John","lastName":"Baeldung","age": 18}' \
http://localhost:9000/
{
"isSuccessful":true,
"body":{
"firstName":"John",
"lastName":"Baeldung",
"age":18,
"id":0
}
}
名を「Andrew」に設定し、年齢を30に設定して、レコードを更新しましょう。
curl -X PUT -H "Content-Type: application/json" \
-d '{"firstName":"Andrew","lastName":"Baeldung","age": 30,"id":0}' \
http://localhost:9000/
{
"isSuccessful":true,
"body":{
"firstName":"Andrew",
"lastName":"Baeldung",
"age":30,
"id":0
}
}
上記のテストは、レコードを更新した後のfirstNameおよびageフィールドの値の変化を示しています。
追加のダミーレコードをいくつか作成しましょう。JohnDoeとSamBaeldungの2つを追加します。
curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"John","lastName":"Doe","age": 18}' \
http://localhost:9000/
curl -X POST -H "Content-Type: application/json" \
-d '{"firstName":"Sam","lastName":"Baeldung","age": 25}' \
http://localhost:9000/
それでは、すべてのレコードを取得しましょう。
curl -X GET http://localhost:9000/
{
"isSuccessful":true,
"body":[
{
"firstName":"Andrew",
"lastName":"Baeldung",
"age":30,
"id":0
},
{
"firstName":"John",
"lastName":"Doe",
"age":18,
"id":1
},
{
"firstName":"Sam",
"lastName":"Baeldung",
"age":25,
"id":2
}
]
}
上記のテストにより、listStudentsコントローラーアクションが適切に機能していることを確認しています。
7. 結論
この記事では、PlayFrameworkを使用して本格的なRESTAPIを構築する方法を示しました。
いつものように、このチュートリアルのソースコードはGitHubでから入手できます。