JAX-RSのレスポンス
このエントリはJava EE Advent Clanedar2014の16日目の記事です。
昨日は@emaggameさんの「Arquillian Cube を試す #javaee」でした。
明日は@n_agetsuさんです。
【事前準備】
[pom.xml]
mavenのdependencyは下記のとおりです。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <dependencies> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies> </project>
[Application]
javax.ws.rs.ApplicationPathの設定はパスの設定だけです。
今回は コンテキストパス + "/api"をJAX-RSのパスの設定とします。
package jp.glory.advent2014; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/api") public class ApplicationSetting extends Application { }
[データ用Bean]
レスポンスとして返却するためのBeanです。
XMLとして返すので@XmlRootElementをつけます。
lastNameとfirstNameはエレメントなのでセッターに@XmlElementをつけています。
package jp.glory.advent2014; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class TestData { private final String lastName = "Yamada"; private final String firstName = "Taro"; @XmlElement public String getLastName() { return lastName; } @XmlElement public String getFirstName() { return firstName; } }
【シンプルな形で返す】
JAX-RSは特に難しい設定をすることなく、JSON/XMLなどで返却することができます。
レスポンスのTypeは@Producesで指定します。
package jp.glory.advent2014.resources; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/hoge") public class ResponseResource { @GET @Path("/default1") public TestData getDefault1() { return new TestData(); } @GET @Path("/default2") public String getDefault2() { return "aaa"; } @GET @Path("/json") @Produces(MediaType.APPLICATION_JSON) public TestData getJson() { return new TestData(); } @GET @Path("/xml") @Produces(MediaType.APPLICATION_XML) public TestData getXml() { return new TestData(); } }
アノテーションを設定する以外は特に必要ありません。
何も指定しなかった場合、型によって判定されます。
今回はBeanに@XmlRootElementを指定しているのでXMLが返却されます。
文字列の場合はHTMLとして返却されます。
「http://localhost:8080/${contetPath}/api/hoge/xxx」にアクセスするとレスポンスが帰ってくると思います。
(xxxには「default1」、「default2」などを入れてください)
【JerseyMVC】
ここからはJAX-RSの実装の一つであるJerseyMVCの独自機能性になりますが、
JAX-RSで従来のMVCパターンを実現することができます。
[pom.xml]
pom.xmlは下のように変えます。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <dependencies> <dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc-jsp</artifactId> <version>2.12</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> </dependencies> </project>
dependenciesをJeseyMVCとServleet2.5に変更しています。
[Application]
先ほど作成したApplicationSettingには少しだけJerseyMVC独自のコードを書きます。
package jp.glory.advent2014; import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.mvc.jsp.JspMvcFeature; @ApplicationPath("/api") public class ApplicationSetting extends ResourceConfig { public ApplicationSetting() { packages(this.getClass().getPackage().getName()); register(JspMvcFeature.class); } }
まずは継承するクラスをResourceConfigに変更します。
コンストラクタ内部では初期設定を行います。
packagesメソッドでリソースクラスがどのパッケージにあるかを指定します。
今回は「jp.glory.advent2014」の下全てと言うことになります。
registerメソッドでJerseyMVCのコンポーネントを登録します。
今回はJSPと連携するためのJspMvcFeatureクラスだけを登録しています。
[web.xml]
JerseyMVCを使用する場合にはweb.xmlの設定が必要になります。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>AdventSampleFilter</filter-name> <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>jp.glory.advent2014.ApplicationSetting</param-value> </init-param> <init-param> <param-name>jersey.config.servlet.filter.contextPath</param-name> <param-value>api</param-value> </init-param> <init-param> <param-name>jersey.config.server.mvc.templateBasePath.jsp</param-name> <param-value>/WEB-INF</param-value> </init-param> </filter> <filter-mapping> <filter-name>AdventSampleFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
FilterとしてJerseyのServletContainerを設定します。
初期パラメータは設定値は下のようになります。
パラメータ名 | 設定内容 |
---|---|
javax.ws.rs.Application | ResourceConfigクラスを継承したクラスのフルネームを設定します。 |
jersey.config.servlet.filter.contextPath | ApplicationPathで指定した内容を設定します。 |
jersey.config.server.mvc.templateBasePath.jsp | JSPファイルの配置場所を設定します |
[JSP]
JSPはWEB-INF直下におきます。
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP Page</title> </head> <body> <dl> <dt>LastName</dt> <dd>${it.lastName}</dd> <dt>FirstName</dt> <dd>${it.firstName}</dd> </dl> </body> </html>
JAX-RSから渡されるオブジェクトはitと言う名前で登録されるので、
JSPでは${it.hoge}でアクセスするようにします。
[リソースクラス]
最後にリソースクラスの修正です。
JSP用のURLとして「/api/hoge/jsp」を作成します。
package jp.glory.advent2014.resources; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import jp.glory.advent2014.TestData; import org.glassfish.jersey.server.mvc.Viewable; @Path("/hoge") public class ResponseResource { // 先ほど作成したメソッドは変わっていないので省略 @GET @Path("/jsp") public Viewable getJsp() { return new Viewable("/sample.jsp", new TestData()); } }
Viewableのオブジェクトに
JSPファイルのパスとモデルオブジェクト(今回はTestDataクラスのオブジェクト)を渡すだけです。
「http://localhost:8080/Advent2014/api/hoge/jsp」にアクセスすると
モデルオブジェクトで設定した値が反映され、画面が表示されます。
【まとめ】
このようにJAX-RSは主な用途であるREST APIを簡単に実装でき、
さらにJerseyMVCを使えばリソースクラスのつくりをほとんど変えずにJSPとも連携ができます。
あくまでJerseyMVCであって、正式なJava EEの仕様ではありませんが、
個人的にはJavaでWebと言った場合に
最初に検討しても良いフレームワークなのかなと思います。
JerseyMVCで提供されているのは Mustache/Freemarker/JSPのみですが、
他のテンプレートエンジンにも適用することができます。
JerseyMVCとThymeleafを組み合わせる - シュンツのつまづき日記
また、MVC1.0(JSR371)でJAX-RSと組み合わせられるようにするかという議論もあるようです。
個人的にはMVC1.0がJAX-RSと組み合わせやすいようになれば
さらにJAX-RSを採用する理由が増えるのかなと思っています。