はじめに
サンプルプログラムから JAX-RS を使った実用的な RESTful サービスの作り方を学んでいく。
対象と環境
Java でWEBアプリケーションの知識がある人を対象としている。
Java EE の知識は不要。Spring 等のフレームワークの知識も不要。
実際に動く環境の構築方法を別途まとめてある。
Jerseyのbookmarkサンプルを動かす-java RESTful service-
特殊なものは何も使っていないので誰でも無償で構築できる。
是非、実際に動かしながら理解を深めていただきたい。
まずは基本
サンプルの提供元は以下なので、これで理解できれば、この記事を読む必要は無い。
Jerseyブックマーク・サンプルについて
RESTful Webサービスの開発
補足の意味で、某大手システムインテグレータのサイト
JAX-RSアプリケーションの作成方法
JAX-RSアプリケーションの作成時には、以下の2種類のクラスをコーディングする。
- Applicationサブクラス
- リソースクラス
基本 Servlet なので web.xml に設定を行う方法もあるが、
サンプルアプリでは使っていない。アノテーションを活用している。
Applicationサブクラス
web.xml に設定を書けばこのクラスは不要である。
ただ、後々いろいろな意味でプログラムで書いた方が解り易い。
サンプルプログラムの MyApplication.java では、
@ApplicationPath("/api")
public class MyApplication extends ResourceConfig {...}
JAX-RS の実装として Jersey を使っているので、ResourceConfig を継承しているが、標準としては、Application を継承する。(もちろんJerseyでも使える)
@ApplicationPath("/api")
public class MyApplication extends Application {...}
このクラスで行う事はWEBコンテナが起動した際に一度だけ実行される内容になる。
重要なのは、@ApplicationPathであり、これがリソースクラスを呼び出す URI の基底となる。URI については後で詳しく説明する。
リソースクラス
API を実行した際に呼び出される中核の処理を書く。
サンプルプログラムの UsersResource.java を見てみよう。
@Path("/users/")
public class UsersResource {...}
何でもない普通のクラス。
@Path アノテーションが指定されている事に意味がある。
さらにクラス内のメソッドには次がある。
@GET
@Produces("application/json")
public JSONArray getUsersAsJsonArray() {
JSONArray uriArray = new JSONArray();
for (UserEntity userEntity : getUsers()) {
UriBuilder ub = uriInfo.getAbsolutePathBuilder();
URI userUri = ub
.path(userEntity.getUserid())
.build();
uriArray.put(userUri.toASCIIString());
}
return uriArray;
}
@GET でこのメソッドがクラスの Path を含む URI で参照された際に呼び出される。
試しに /users/ を Swagger で呼び出してみよう。
この様なユーザーの一覧のレスポンスが返ってくる。
サブリソースに対する @Path の使い方
リソースのクラスに@Path を指定するのに対して、リソース内のメソッドに指定した場合、サブリソースとして処理される。
@Path("{userid}/")
public UserResource getUser(@PathParam("userid") String userid) {...}
@Path("{userid}/") の {userid} はパラメータ名として扱われ、@PathParam で取得できる。
つまり、URI として
/api/users/123
の様に指定すれば、userid が 123 を参照するリクエストになる。
URI の一部を変数であるように、簡単に扱えるのも JAX-RS の利点である。
実際には、処理を UserResource.java のクラスに丸投げしているので、それがサブリソースの実体ということになる。
このように入れ子にしていけば、いくらでも URI を深くする事が出来る。
サンプルではさらに、/bookmarks/{bmid} と深くなっている。
実行時の基底URIの構成
ここが結構重要で、リソース内の特定のメソッドを呼び出す URI は、
http://myHostName/contextPath/servletURI/resourceURI/subresourceURI/...
- contextPath --- WEBコンテナ(例:Tomcat)のコンテキスト
- servletURI --- @ApplicationPath() で指定
- resourceURI --- @Path() をクラスに指定
- subresourceURI --- @Path() をクラス内のメソッドに指定
という関係になる。
例えば、サブリソースである UserResource.java にある@GET のメソッドは
@GET
@Produces("application/json")
public JSONObject getUser() throws JSONException {
if (null == userEntity) {
throw new ExtendedNotFoundException("userid " + userid + "does not exist!");
}
return new JSONObject().put("userid", userEntity.getUserid())
.put("username", userEntity.getUsername())
.put("email", userEntity.getEmail())
.put("password", userEntity.getPassword())
.put("bookmarks", uriInfo.getAbsolutePathBuilder().path("bookmarks").build());
}
次のように呼び出せせる。
http://localhost:8081/bookmark40/api/users/1
Swagger で確認すると
という結果が返って来る。
どのメソッドが呼び出されるか
各リソース内のどのメソッドが呼ばれるかは、http のリクエストメソッドによって決まる。
受信HTTPリクエストのJavaメソッドへのマッピング
さらに、約束事で @PUT は追加と更新を処理するメソッドを呼び出すようにする。
どのメソッドでどういう処理が行われるかがちゃんと守られれば利用者が迷わなくて済む。
リクエスト・メッセージからの情報の抽出
http のリクエストを解析するのは手間がかかる。
@PathParam のように、良く使うものはアノテーションで用意されている。
リクエスト・メッセージからの情報の抽出
これらを使えば、コーディング量を減らして見やすいコードにすることができる。
メディア・タイプ
簡単に言えば、入出力(http のリクエストとレスポンス)の形式のこと。
- @Consumes がリクエスト
- @Produces がレスポンス
現実には、今は JSON が主流であるからあえて深く考える必要も無い話ではある。
あえて言えば、リクエストメソッドに POST を使う際、従来の html の <FORM> タグで submit すると、Content-Type: application/x-www-form-urlencoded で送信されるので JSON 形式で受け取ろうとするとミスマッチになる。
それをいちいちプログラムを書いて検出するのは面倒なので、専用のアノテーションを使う事で手間が省ける。それが、@Consumes の役割である。
同様に、クライアントがレスポンスで別の形式を要求してきた場合、エラーにしてくれるのが @Produces である。
レスポンス・コード
JSON を返すのは良いが、@PUT や @DELETE はわざわざメッセージを返す必要は無くステータスコードだけ返せば良い。
その為にも方法が用意されている。
@GET はデフォルトで「200 OK」を返すのでわざわざ書く必要はないが、
サンプルの @PUT の処理では、
@PUT
public Response putUser(JSONObject jsonEntity) throws JSONException {
:
if (newRecord) {
:
return Response.created(uriInfo.getAbsolutePath()).build();
} else {
:
return Response.noContent().build();
}
}
となっており、追加と更新では別のコードを返している。追加処理を Swagger で見ると、
の様なレスポンスを返している。
最後に
以上が概要で、細かい点はより詳細なドキュメントを参照して欲しい。
また、実際に動かして確認してみることが大事だ。
要点を押さえれば、RESTful API サービスを構築するのは難しくはない。