前回の
からの続き。
このエントリではRESTっぽくGET/POSTするところまでまとめた。
ここでやること
POST /hoges
でHogeデータを登録する。GET /hoges
でHogeデータのリストを取得する。GET /hoges/{id}
でHogeデータを取得する。- データストアはJavaのメモリ上に持つ。
- 本来ならDB保存が殆どだろうけど、本題じゃないので横着。
- 本来ならスレッドセーフにすべきだけど、本題じゃないので横着。
- データは XML or JSON で返す。
環境 (前回エントリからの差分)
いくつか新しいバージョンが出てたのでインストールし直した。
- JDK 7 u67 => JDK 8 u25
- Maven 3.2.2 => Maven 3.2.3
- GlassFish 4.0.1 => GlassFish 4.1
依存ライブラリ Jersey core Servlet 3.x 追加
まずは GlassFish のサーブレットベースで動かすのに必要となる、
Jersey のライブラリをクラスパスに追加してあげる。
この内容を pom.xml
の依存関係に追加すればOK。
GlassFish には元から Jersey が搭載されてるので、 scope
は provided
にする。
<project ..> ... <dependencies> ... <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version>2.10.4</version> <scope>provided</scope> </dependency> ... </dependencies> ... </project>
依存ライブラリ Lombok 追加
今回のお題とは関係ないのだけど、
後でGetter/Setterを楽に作れるように、Lombokを使えるようにしておく。
この内容を pom.xml
の依存関係に追加すればOK。
コンパイル時にだけ動けば良いので、 scope
は provided
にする。
<project ..> ... <dependencies> ... <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.14.8</version> <scope>provided</scope> </dependency> ... </dependencies> ... </project>
Jersey ServletContainer 設定
Jersey を動かす方法には、
- サーブレットで動かす方法:
web.xml
に書く - サーブレットで動かす方法:
@ApplicationPath
を使う - フィルタで動かす方法:
web.xml
に書く
があるっぽい。
このうち、ここではフィルタで動かす方法を選んだ。
後々は "Jersey JSP MVC Templates" ってので
レスポンスのレンダリングもやってみたいと考えてるのだけど、
これを使うには ServletFilter を web.xml に登録しないといけない らしいので。
フィルタで動かすには、下記内容を web.xml
に追加すればOK。
tryrest.TryRestApplication
の部分には、
この後自分の作成するクラスの名前を指定しておく。
<web-app ...> ... <filter> <filter-name>servlet-container</filter-name> <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>tryrest.TryRestApplication</param-value> </init-param> </filter> <filter-mapping> <filter-name>servlet-container</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
ちなみに、これを書くと src/main/webapp
配下に置いてるJSPは
フィルタで覆われて見れなくなってしまう。
もちろん見れるようにも設定出来るけど、それはまた別でまとめたい。
アプリケーションクラス 作成
javax.ws.rs.core.Application
を継承したクラスを作る。
org.glassfish.jersey.server.ResourceConfig
(Application
継承) を使うと、
色々メソッドが生えてて便利。
このクラスでは利用するリソースクラス等の登録が責務っぽい。
ここでは ResourceConfig#packages()
を使って、
自分のパッケージ配下をスキャン&登録して貰う。
package tryrest; import org.glassfish.jersey.server.ResourceConfig; public class TryRestApplication extends ResourceConfig { public TryRestApplication() { packages(TryRestApplication.class.getPackage().getName()); } }
リソースクラス 作成
今回メインとなるリソースクラスを作る。
URLとのマッピングと、実処理を書く。
ポイントはこんなとこ。
@Path
でURLのマッピングをする。@Path
はメソッドにも付けれる。- その場合は、クラスとメソッドのパスを連結したURLになる。
{hogehoge}
でURLパスの一部をパラメータに出来る (=>@PathParam
)。
@GET
,@POST
で HTTP メソッドのマッピングをする。- パラメータは引数やフィールドで受取れる。
- URLパスの一部を受取る場合は、
@PathParam
を使う。 - URLクエリ文字列を受取る場合は
@QueryParam
を使う。 - フォームの値を受取る場合は
@FormParam
を使う。 - Beanクラスにまとめて受取る場合は
@BeanParam
を使う。
- URLパスの一部を受取る場合は、
- 戻り値のデータは、リクエストヘッダに応じたフォーマットで返してくれる。
Accept: application/json
なら JSON に変換して返してくれる。Accept: application/xml
なら XML に変換して返してくれる。
@Context
を使うと色々なリクエスト/レスポンス情報をインジェクション出来る。- 今回は、アプリケーションのURLを取得するのに
UriInfo
を使ってる。
- 今回は、アプリケーションのURLを取得するのに
package tryrest.hoges; import java.net.URI; import java.util.ArrayList; import java.util.List; import javax.ws.rs.BeanParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; @Path("/hoges") public class HogeResource { private static List<HogeData> store = new ArrayList<HogeData>(); @Context private UriInfo uriInfo; @POST public Response post(@BeanParam HogeData data) { store.add(data); URI uri = uriInfo.getAbsolutePathBuilder().path("{id}").build(store.size()); return Response.created(uri).entity(data).build(); } @GET public List<HogeData> get() { return store; } @GET @Path("/{id}") public HogeData get(@PathParam("id") int id) { return store.get(id - 1); } }
データクラスはこんな感じ。
単純な Bean に幾つかのアノテーションを与えてる。
@Data
は Getter/Setter を自動生成してる。- Lombok の機能。コードの見通し良くなって Lombok 最高。
toString()
なんかも自動生成してくれる。
@XmlRootElement
は XML に変換する場合に必要。- JAXB の機能。
- 他にも色々アノテーションがあって、XMLの形をカスタマイズ出来るっぽい。
@FormParam
はフォームデータのキーとのマッピング。- リストの場合は、同じキーのデータを複数送ればOK。
package tryrest.hoges; import java.util.List; import javax.ws.rs.FormParam; import javax.xml.bind.annotation.XmlRootElement; import lombok.Data; @Data @XmlRootElement public class HogeData { @FormParam("integer") private Integer integer; @FormParam("string") private String string; @FormParam("strings") private List<String> strings; }
コンパイル, パッケージ作成, デプロイ
ここまで出来たらコンパイル&デプロイ。
$ mvn compile package glassfish:redeploy
動作確認
まずは POST で適当なデータを登録してみる。
レスポンスはJSONで。
$ curl -i -H "Accept: application/json" \ -d "integer=1" \ -d "string=aaa" \ -d "strings=AAA1" -d "strings=AAA2" -d "strings=AAA3" \ http://your-host:8080/try-rest/hoges HTTP/1.1 201 Created Server: GlassFish Server Open Source Edition 4.1 X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.1 Java/Oracle Corporation/1.8) Location: http://your-host:8080/try-rest/hoges/1 Content-Type: application/json Date: Mon, 27 Oct 2014 13:39:26 GMT Content-Length: 61 {"integer":1,"string":"aaa","strings":["AAA1","AAA2","AAA3"]}
次はレスポンスをXMLにしてみる。
リクエストヘッダの Accept
でコントロール。
$ curl -i -H "Accept: application/xml" \ -d "integer=2" \ -d "string=bbb" \ -d "strings=BBB1" -d "strings=BBB2" -d "strings=BBB3" \ http://your-host:8080/try-rest/hoges HTTP/1.1 201 Created Server: GlassFish Server Open Source Edition 4.1 X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.1 Java/Oracle Corporation/1.8) Location: http://your-host:8080/try-rest/hoges/2 Content-Type: application/xml Date: Mon, 27 Oct 2014 13:40:26 GMT Content-Length: 185 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><hogeData><integer>2</integer><string>bbb</string><strings>BBB1</strings><strings>BBB2</strings><strings>BBB3</strings></hogeData>
今度はデータのリストを JSON で GET してみる。
$ curl -i -H "Accept: application/json" \ http://your-host:8080/try-rest/hoges HTTP/1.1 200 OK Server: GlassFish Server Open Source Edition 4.1 X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.1 Java/Oracle Corporation/1.8) Content-Type: application/json Date: Mon, 27 Oct 2014 13:40:56 GMT Content-Length: 125 [{"integer":1,"string":"aaa","strings":["AAA1","AAA2","AAA3"]},{"integer":2,"string":"bbb","strings":["BBB1","BBB2","BBB3"]}]
単一のデータも GET してみる。id=2 のデータを XML で。
$ curl -i -H "Accept: application/xml" \ http://your-host:8080/try-rest/hoges/2 HTTP/1.1 200 OK Server: GlassFish Server Open Source Edition 4.1 X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.1 Java/Oracle Corporation/1.8) Content-Type: application/xml Date: Mon, 27 Oct 2014 13:41:28 GMT Content-Length: 185 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><hogeData><integer>2</integer><string>bbb</string><strings>BBB1</strings><strings>BBB2</strings><strings>BBB3</strings></hogeData>
問題なく動いた。
XML/JSON の切換えも出来てる。
Accept ヘッダが面倒な場合は、 URL の拡張子で制御する仕組みもあった。
その辺もまた別でまとめたいなー。
以上!
コード (GitHub)
Release rbox-20141028 - akihyro/try-jaxrs
参考
- 今どきのJava Webフレームワークってどうなってるの? - きしだのはてな
- Jersey 2.13 User Guide
- Project Lombok
- JerseyMVCの使い方メモ - Qiita