The Java EE 7 Tutorialの29 Building RESTful Web Services with JAX-RSの章をテキトーに訳した。
29 Building RESTful Web Services with JAX-RS
この章では、RESTアーキテクチャ・RESTfule webサービス・Java API for RESTful Web Services (JAX-RS、JSR 339で定義)、を解説します。
JAX-RSにより、開発者はJava言語を使用してRESTfule webサービスを簡単に作成できます。
この章では以下のトピックを扱います。
- What Are RESTful Web Services?
- Creating a RESTful Root Resource Class
- Example Applications for JAX-RS
- Further Information about JAX-RS
29.1 What Are RESTful Web Services?
RESTful webサービス(RESTful web services)は、疎結合で軽量なwebサービスで、インターネット経由のクライアント用にAPIを作成するのにとりわけ適しています。Representational State Transfer (REST)はクライアントサーバアプリケーションのアーキテクチャスタイルの一つで、リクエストとレスポンスを通してリソース(resources)の表現(representations)を交換(transfer)することが中心となっている概念です。RESTアーキテクチャスタイルでは、データと機能はリソースとみなされ、また、一般的にはWebのリンクであるUniform Resource Identifiers (URIs)を使用してアクセスします。リソースはドキュメントによって表現され、単純で明確に定義された操作の組み合わせによって変更されます。
たとえば、RESTリソースには都市の現在の天候状況が考えられます。このリソースの表現になりうるのは、XMLドキュメント・画像ファイル・HTMLページ、などです。クライアントは特定の表現を取得したり、データ更新によってリソースを修正したり、リソースを完全に削除できたりします。
RESTアーキテクチャスタイルは、HTTPのような、ステートレスな通信プロトコルを使用するように設計されています。RESTアーキテクチャスタイルでは、クライアントとサーバは標準化されたインタフェースとプロトコルを使用してリソース表現を交換します。
以下の原則がRESTfulアプリケーションをシンプルで軽量で高速になるように促します。
- Resource identification through URI: RESTful webサービスはクライアントとの通信ターゲットを一意に特定するためのリソースセットを公開します。URIによってリソースは一意に指定され、リソースとサービスディスカバリ用のグローバルなアドレス空間を提供します。詳細な情報についてはThe @Path Annotation and URI Path Templatesを参照してください。
- Uniform interface: リソースは4つの操作、生成(create)・読み込み(read)・更新(update)・削除(delete)に固定されており、PUT, GET, POST, DELETEに相当します。PUTは新しいリソースを生成、そのリソースはDELETEで削除可能です。GETは表現におけるリソースの現在の状態を取得します。POSTはリソースの新しい状態を転送します。詳細な情報についてはResponding to HTTP Methods and Requestsを参照してください。
- Self-descriptive messages: リソースと表現は切り離されており、その理由はコンテンツは様々なフォーマットでアクセスされるためで、たとえば、HTML, XML, プレーンテキスト, PDF, JPEG, JSONまたはその他のドキュメントフォーマットなどです。リソースに関するメタデータが利用可能で、キャッシュコントロール・通信エラーの検出・適切な表現フォーマットのネゴシエート・認証やアクセス制御の実行、に使用されます。詳細な情報についてはResponding to HTTP Methods and RequestsとUsing Entity Providers to Map HTTP Response and Request Entity Bodiesを参照してください。
- Stateful interactions through links: リソースとのすべての通信はステートレスで、リクエストメッセージは自己完結しています。ステートフルな通信は明示的な状態転送のコンセプトに基づいています。状態をやり取りするための技術としては、URIリライティング、Cookie、hiddenフィールドなどがあります。状態は、以降の通信の状態を検証するために、レスポンスメッセージに埋め込みが可能です。詳細な情報については、Using Entity Providers to Map HTTP Response and Request Entity BodiesとJAX-RS Overviewの"Building URIs"を参照してください。
29.2 Creating a RESTful Root Resource Class
Root resource classesは、@Pathアノテーションを持つ"plain old Java objects" (POJOs)か、少なくとも一つの@Pathアノテーションが付与されたメソッドを持つクラス
Resource methodsは、request method designatorアノテーションを付与されたリソースクラスのメソッドです。このセクションではRESTful webサービスを作成するためにJavaクラスに付与するアノテーションの使用方法について説明します。
29.2.1 Developing RESTful Web Services with JAX-RS
JAX-RSは、RESTアーキテクチャを使用するアプリケーションをJavaプログラミング言語のAPIを使用して簡単に作成できるように設計されたものです。
JAX-RS APIはRESTful webサービスの開発をシンプルにするためにJavaプログラミング言語のアノテーションを使用します。開発者はリソースと実行するアクションを定義するのにJAX-RSアノテーションをJavaプログラミング言語のクラスファイルに付与します。JAX-RSアノテーションはランタイムアノテーションです。それゆえに、ランタイムのリフレクションはヘルパークラスとリソースのアーティファクトを生成します。JAX-RSリソースクラスが含まれるJava EEアプリケーションアーカイブは、設定済みリソース、ヘルパークラスと生成されたアーティファクトを持ち、Java EEサーバにアーカイブをデプロイすることでクライアントにリソースを公開します。
表 29-1はJAX-RSが定義するアノテーションと使用方法の要約のリストです。JAX-RS APIの詳細な情報については http://docs.oracle.com/javaee/7/api/ を参照してください。
| アノテーション | 概要 |
|---|---|
@Path |
@Pathの値にはこのJavaクラスがホストする相対URIパスを指定します。たとえば/helloworld。また、URIパステンプレートでURIに組み込み変数を使用可能です。たとえば、ユーザ名をURIの変数としてアプリケーションに渡すことが出来ます。/helloworld/{username} |
@GET |
@GETはrequest method designatorで同名のHTTPメソッドに相当します。このrequest method designatorのアノテーションが付与されたJavaのメソッドはHTTP GETリクエストを処理します。リソースの振る舞いは、リソースが応答するためのHTTPメソッドによって決定されます。 |
@POST |
@POSTはrequest method designatorで同名のHTTPメソッドに相当します。このrequest method designatorのアノテーションが付与されたJavaのメソッドはHTTP POSTリクエストを処理します。リソースの振る舞いは、リソースが応答するためのHTTPメソッドによって決定されます。 |
@PUT |
@PUTはrequest method designatorで同名のHTTPメソッドに相当します。このrequest method designatorのアノテーションが付与されたJavaのメソッドはHTTP PUTリクエストを処理します。リソースの振る舞いは、リソースが応答するためのHTTPメソッドによって決定されます。 |
@DELETE |
@DELETEはrequest method designatorで同名のHTTPメソッドに相当します。このrequest method designatorのアノテーションが付与されたJavaのメソッドはHTTP DELETEリクエストを処理します。リソースの振る舞いは、リソースが応答するためのHTTPメソッドによって決定されます。 |
@HEAD |
@HEADはrequest method designatorで同名のHTTPメソッドに相当します。このrequest method designatorのアノテーションが付与されたJavaのメソッドはHTTP HEADリクエストを処理します。リソースの振る舞いは、リソースが応答するためのHTTPメソッドによって決定されます。 |
@PathParam |
@PathParamはリソースクラスでパラメータを抽出するために使用するアノテーションです。URIパスのパラメータをリクエストURIから抽出し、パラメータ名が@Pathクラスレベルアノテーションで指定されているURIパステンプレート変数名に対応します。 |
@QueryParam |
@QueryParamはリソースクラスでパラメータを抽出するために使用するアノテーションです。クエリパラメータはリクエストURIクエリパラメータから抽出します。 |
@Consumes |
@Consumesはクライアントが送信するもののうちリソースが受け入れ可能な表現のMIMEタイプを指定するために使用します。 |
@Produces |
@Producesはクライアントへの返信とリソースが生成可能な表現のMIMEタイプを指定するために使用します。例としてはtext/plain。 |
@Provider |
@ProviderはMessageBodyReaderとMessageBodyWriterなどをJAX-RSランタイムに設定するために使用します。HTTPリクエストでは、MessageBodyReaderはHTTPリクエストのエンティティボディをメソッドパラメータにマップするために使用します。対してレスポンスでは、MessageBodyWriterを使用して戻り値をHTTPレスポンスのエンティティボディにマップします。アプリケーションが、HTTPヘッダーや異なるステータスコードなどの追加のメタデータの提供が必要な場合、Response.ResponseBuilderを使用して構築可能な、エンティティをラップするResponseを戻り値に取ることが出来ます。 |
@ApplicationPath |
@ApplicationPathはアプリケーションのURLマッピングを定義するために使用します。@ApplicationPathで指定するパスはベースURIで、リソースクラスの@Pathアノテーションで指定されるすべてのリソースURIのベースとなります。@ApplicationPathはjavax.ws.rs.core.Applicationのサブクラスにのみ適用可能です。 |
29.2.2 Overview of a JAX-RS Application
以下のコードはJAX-RSアノテーションを使用するルートリソースクラスのごく単純な例です。
package javaeetutorial.hello; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; /** * ルートリソース ("helloworld"パスで公開される) */ @Path("helloworld") public class HelloWorld { @Context private UriInfo context; /** HelloWorldのインスタンスを作成する。 */ public HelloWorld() { } /** * helloWorld.HelloWorldインスタンスの表現を取得する。 * @return java.lang.Stringのインスタンス */ @GET @Produces("text/html") public String getHtml() { return "<html lang=\"en\"><body><h1>Hello, World!!</h1></body></html>"; } }
以降のセクションでこのサンプルで使われているアノテーションについて説明します。
@Pathの値は相対URIのパスです。この例では、JavaクラスはURIパス/helloworldにホストされます。これは最も単純な@Pathの使用法で、静的なURIパスを使用しています。変数をURIに埋め込み可能で、URIパステンプレート(URI path templates)のURIにはシンタックス内に変数を埋め込み可能です。@GETは、JAX-RSが定義する@POST,@PUT,@DELETE,@HEADと同様なrequest method designatorで、同名のHTTPメソッドに相当します。この例では、アノテーションが付与されたJavaのメソッドはHTTP GETリクエストを処理します。リソースの振る舞いは、リソースがレスポンスを行うHTTPメソッドによって決定されます。@Producesはクライアントに送り返すためにリソースが生成可能なMIMEメディアタイプを指定するために使用します。この例では、JavaメソッドはMIMEメディアタイプ"text/html"で指定される表現を生成します。@Consumesはクライアントが送信してリソースが受け入れ可能なMIMEメディアタイプを指定するために使用します。上記の例を修正してgetHtmlメソッドが返すメッセージを格納するよう、以下のように修正可能です。
@POST @Consumes("text/plain") public void postHtml(String message) { // Store the message }
29.2.3 The @Path Annotation and URI Path Templates
@Pathにはリソースが応答するためのURIパステンプレートを指定し、また、リソースのクラスもしくはメソッドレベルに付与します。@Pathの値は、URIパステンプレートです。また、リソースがデプロイされるサーバのベースURIと、JAX-RSランタイムがレスポンスを返すためのURLパターンと、アプリケーションのコンテキストルートに対し、相対的です。
URIパステンプレートはURIシンタックス内に埋め込まれる変数付きのURIです。これらの変数は、置換されるURLに基づいてリソースがリクエストに応答するためにランタイムが置換します。変数は中括弧({と})で指定します。以下は@Pathの例です。
@Path("/users/{username}")
この例では、ユーザは彼or彼女の名前をタイプし、それからこのURIパステンプレートへのリクエストに応答できるように設定されたJAX-RS webサービスがレスポンスを返します。ユーザがユーザ名`Galileo"とタイプしたら、webサービスは以下のURLにレスポンスを返します。
http://example.com/users/Galileo
ユーザ名を取得するには、以下のようにリクエストメソッドのパラメータに@PathParamを使用します。
@Path("/users/{username}") public class UserResource { @GET @Produces("text/xml") public String getUser(@PathParam("username") String userName) { ... } }
デフォルトでは、URL変数は正規表現"[^/]+?"にマッチします。この変数は、変数名の後ろに異なる正規表現を指定することでカスタマイズが可能です。たとえば、ユーザ名を小文字と大文字数字のみで構成したい場合、変数定義のデフォルトの正規表現をオーバーライドします。
@Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}")
この例では、username変数は、一文字目が大文字か小文字で始まり、二文字目以降がゼロか英数字とアンダースコアで始まる場合のみマッチします。ユーザ名がテンプレートにマッチしない場合、クライアントには404 (Not Found)レスポンスが返されます。
@Pathは、スラッシュ(/)を先頭や末尾に持つ必要はありません。JAX-RSランタイムは、先頭や末尾にスラッシュを持つかどうかに関わらず、同じ結果になるようにURIパステンプレートをパースします。
URIパステンプレートは一つ以上の変数を持ち、各変数は中括弧で囲みます。変数名は{で始めて}で終わります。先の例では、usernameが変数名です。URIパステンプレートにレスポンスを返すよう設定されたリソースは、URI中の{username}に対応するURIデータをusername変数として、ランタイムが処理します。
たとえば、URIパステンプレートhttp://example.com/myContextRoot/resources/{name1}/{name2}/に相当するリソースをデプロイしたいとすると、まずhttp://example.com/myContextRootURIでリクエストとレスポンスが可能なアプリケーションをJava EEサーバーにデプロイし、リソースに以下の@Pathアノテーションを付与します。
@Path("/{name1}/{name2}/") public class SomeResource { ... }
この例では、JAX-RSヘルバーサーブレット用のURLパターンを、web.xmlで指定します。
<servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping>
変数中の文字がURLの予約語と衝突する場合、その文字はパーセントでエンコードされたもので置換されます。たとえば、変数のスペースは%20に置換されます。
URLパステンプレートを定義するとき、置換後のURLが有効になるように注意してください。
表 29-2は、URIパステンプレートの例と、どのようにURLが置換後に解決されるか、の例を示しています。以下の変数名と値が例で使用されます。
name1:jamesname2:gatzname3:location:Main%20Streetquestion:why
Note: name3の値は空文字です。
表 29-2 URIパステンプレートの例
| URIパステンプレート | 置換後のURI |
|---|---|
http://example.com/{name1}/{name2}/ |
http://example.com/james/gatz/ |
http://example.com/{question}/{question}/{question}/ |
http://example.com/why/why/why/ |
http://example.com/maps/{location} |
http://example.com/maps/Main%20Street |
http://example.com/{name3}/home/ |
http://example.com//home/ |
29.2.4 Responding to HTTP Methods and Requests
リソースの振る舞いはHTTPメソッド(一般的には、GET, POST, PUT, DELETE)が決定します。
29.2.4.1 The Request Method Designator Annotations
Request method designatorアノテーションは、JAX-RSが定義するランタイムアノテーションで、同名のHTTPメソッドに相当します。リソースクラスファイル内では、HTTPメソッドはrequest method designatorを付与されたJavaのメソッドにマップされます。リソースの振る舞いは、リソースがレスポンスを返すHTTPメソッドによって決定されます。JAX-RSは一般的なHTTPメソッドであるGET, POST, PUT, DELETE, HEADのrequest method designatorsを定義しています。また、自前のカスタムrequest method designatorsを作ることも出来ますが、そのカスタマイズについては本ドキュメントの対象外です。
以下の例はPUTメソッドを使用するストレージコンテナの作成もしくは更新を示しています。
@PUT public Response putContainer() { System.out.println("PUT CONTAINER " + container); URI uri = uriInfo.getAbsolutePath(); Container c = new Container(container, uri.toString()); Response r; if (!MemoryStore.MS.hasContainer(c)) { r = Response.created(uri).build(); } else { r = Response.noContent().build(); } MemoryStore.MS.createContainer(c); return r; }
デフォルトでは、もし明示的にHEADとOPTIONSが実装されていない場合、JAX-RSランタイムは自動的にそれらのメソッドをサポートします。HEADは、ランタイムは実装済みのGETメソッドが存在すればそれを呼び出し、レスポンスエンティティが設定されていたらそれを無視します。OPTIONは、リソースがサポートするHTTPメソッドの組み合わせをAllowレスポンスヘッダーに設定します。また、JAX-RSランタイムはリソースを記述するWeb Application Definition Language (WADL)を返します。これの詳細については http://www.w3.org/Submission/wadl/ を参照してください。
request method designatorsアノテーションを付与したメソッドの戻り値は、voidかjavax.ws.rs.core.Responseでなければなりません。Extracting Request Parametersで解説したように、複数のパラメータを@PathParamや@QueryParamアノテーションを使用してURIから抽出できます。Javaの型とエンティティボディ間の変換は、MessageBodyReaderやMessageBodyWriterなどの、エンティティプロバイダーの責任です。レスポンスに追加のメタデータが必要なメソッドはResponseクラスのインスタンスを戻り値にします。ResponseBuilderクラスはBuilderパターンを使用してReponseインスタンスを生成する方法を提供します。HTTP PUTとPOSTメソッドはHTTPリクエストボディを持つので、PUTとPOSTリクエストに応答するためにメソッドではMessageBodyReaderを使用します。
@PUTと@POSTはリソースの作成や更新に使用可能です。POSTは多様な意味を持つため、POSTのセマンティクス定義はアプリケーション次第になります。PUTは明確に定義されたセマンティクスを持ちます。生成にPUTを使用するとき、クライアントは新規に生成されたリソースのURIを宣言します。
PUTは作成もしくは更新されたリソースの明確なセマンティクスを持ちます。クライアントが送信する表現は、同一のメディアタイプのGETを使用して受信する表現と同一でなければなりません。PUTメソッドの良くある間違いとしては、PUTはリソースの部分的な更新を許可しません。POSTでリソースを作成する一般的なアプリケーションパターンは、ロケーションヘッダーのレスポンスに201を返し、新規作成されたリソースのURLを値として持ちます。このパターンでは、webサービスは新規作成されたリソースのURIを宣言します。
29.2.4.2 Using Entity Providers to Map HTTP Response and Request Entity Bodies
エンティティプロバイダー(Entity providers)は表現とJavaの型とのマッピングを行います。エンティティプロバイダーにはMessageBodyReaderとMessageBodyWriterの二種類があります。HTTPリクエストでは、MessageBodyReaderがHTTPリクエストエンティティボディとメソッドパラメータのマッピングに使われます。一方、MessageBodyWriterを使用して戻り値はHTTPレスポンスエンティティボディにマッピングされます。もしアプリケーションがHTTPヘッダーや異なるステータスコードなどの追加のメタデータを提供する場合、エンティティをラップするResponseを返すことが可能で、ResponseはResponse.ResponseBuilderを使用して作成します。
表 29-3はHTTPリクエストとレスポンスエンティテイボディで自動的にサポートされる標準タイプを示しています。エンティティプロバイダーは以下の標準タイプを使用できない場合のみ作成する必要があります。
表 29-3 HTTPリクエストとレスポンスエンティテイボディでサポートされる型
| Java Type | Supported Media Types |
|---|---|
byte[] |
すべてのメディアタイプ (*/*) |
java.lang.String |
すべてのテキストメディアタイプ (text/*) |
java.io.InputStream |
すべてのメディアタイプ (*/*) |
java.io.Reader |
すべてのメディアタイプ (*/*) |
java.io.File |
すべてのメディアタイプ (*/*) |
javax.activation.DataSource |
すべてのメディアタイプ (*/*) |
javax.xml.transform.Source |
XMLメディアタイプ (text/xml, application/xml, application/*+xml) |
javax.xml.bind.JAXBElementとアプリケーションが提供するJAXBクラス |
XMLメディアタイプ (text/xml, application/xml, application/*+xml) |
MultivaluedMap<String, String> |
フォームコンテンツ (application/x-www-form-urlencoded) |
StreamingOutput |
すべてのメディアタイプ (/), MessageBodyWriterのみ |
以下の例は@Consumesと@ProviderアノテーションでMessageBodyReaderを使用する方法を示しています。
@Consumes("application/x-www-form-urlencoded") @Provider public class FormReader implements MessageBodyReader<NameValuePair> {
以下の例は@Producesと@ProviderアノテーションでMessageBodyWriterを使用する方法を示しています。
@Produces("text/html") @Provider public class FormWriter implements MessageBodyWriter<Hashtable<String, String>> {
以下の例はResponseBuilderの使用方法を示しています。
@GET public Response getItem() { System.out.println("GET ITEM " + container + " " + item); Item i = MemoryStore.MS.getItem(container, item); if (i == null) throw new NotFoundException("Item not found"); Date lastModified = i.getLastModified().getTime(); EntityTag et = new EntityTag(i.getDigest()); ResponseBuilder rb = request.evaluatePreconditions(lastModified, et); if (rb != null) return rb.build(); byte[] b = MemoryStore.MS.getItemData(container, item); return Response.ok(b, i.getMimeType()). lastModified(lastModified).tag(et).build(); }
29.2.5 Using @Consumes and @Produces to Customize Requests and Responses
リソースへの送信とクライアントに送り返す情報はMIMEメディアタイプとしてHTTPリクエストやレスポンスのヘッダーに指定します。リソースが応答もしくは生成が可能な表現のMIMEメディアタイプを以下のアノテーションで指定可能です。
javax.ws.rs.Consumesjavax.ws.rs.Produces
デフォルトでは、リソースクラスはHTTPリクエストとレスポンスヘッダーで指定される表現のすべてのMIMEメディアタイプを応答もしくは生成が可能です。
29.2.5.1 The @Produces Annotation
@Producesは、リソースがクライアントに返答および生成することができる表現や、MIMEメディタタイプの指定に使用します。@Producesがクラスレベルに適用されている場合、リソースのすべてのメソッドは指定されたMIMEタイプをデフォルトで生成可能です。メソッドレベルに適用されている場合、クラスレベルの@Producesをオーバーライドします。
リソースにクライアントリクエストのMIMEタイプを生成可能なメソッドが存在しない場合、JAX-RSランタイムはHTTP "406 Not Acceptable"エラーを返します。
@Producesの値はMIMEタイプ文字列の配列か、MediaType定数値のカンマ区切りリストです。たとえば、
@Produces({"image/jpeg,image/png"})
以下の例はクラスとメソッドレベル両方に@Producesを適用する方法を示しています。
@Path("/myResource") @Produces("text/plain") public class SomeResource { @GET public String doGetAsPlainText() { ... } @GET @Produces("text/html") public String doGetAsHtml() { ... } }
doGetAsPlainTextメソッドはクラスレベルの@ProducesのデフォルトMIMEタイプになります。doGetAsHtmlメソッドの@Producesはクラスレベルの@Producesをオーバーライドして、メソッドはプレーンテキストではなくHTMLを生成すると指定しています。
また、@Producesはメディタタイプの指定にjavax.ws.rs.core.MediaTypeで定義されている定数値を使うことも出来ます。たとえば、MediaType.APPLICATION_XMLの指定は"application/xml"と指定することと同等です。
@Produces(MediaType.APPLICATION_XML) @GET public Customer getCustomer() { ... }
リソースクラスが一つ以上のMIMEメディタタイプを生成可能な場合、リソースメソッドはクライアントが宣言したメディアタイプに最も適合するものが選択されます。多くの場合、HTTPリクエストのAcceptヘッダーで宣言しているものが最も適合するものになります。たとえば、AcceptヘッダーがAccept: text/plainの場合、doGetAsPlainTextメソッドが実行されます。また、AcceptヘッダーがAccept: text/plain;q=0.9, text/htmlの場合、クライアントはtext/plainとtext/htmlのメディアタイプを受け入れ可能と宣言していますが、後者を選択するのでdoGetAsHtmlメソッドが実行されます。
@Producesに一つ以上のメディアタイプを宣言可能です。以下のコード例はその方法を示しています。
@Produces({"application/xml", "application/json"}) public String doGetAsXmlOrJson() { ... }
application/xmlもしくはapplication/jsonのどちらかのメディアタイプが受け入れ可能な場合に、doGetAsXmlOrJsonメソッドは実行されます。両方ともが等しく受け入れ可能な場合、先に書かれている方が選択されます。先の例は分かりやすくするために明示的にMIMEメディアタイプを参照しています。タイプミスエラーを減らすには、可能な限り定数値を使用します。定数値の詳細についてはjavax.ws.rs.core.MediaTypeのAPIドキュメントを参照してくださ。
29.2.5.2 The @Consumes Annotation
@Consumesはリソースがクライアントから受け入れ可能な表現のMIMEメディアタイプを指定できます。@Consumesがクラスレベルに適用されている場合、すべてのレスポンスメソッドは指定されたMIMEタイプをデフォルトで受け入れます。メソッドレベルに適用されている場合、@Consumesはクラスレベルに適用されている@Consumesをオーバーライドします。
もしリソースがクライアントリクエストのMIMEタイプを受け入れ不可能な場合、JAX-RSランタイムはHTTP 415 ("Unsupported Media Type")エラーを送り返します。
@Consumesの値は受け入れ可能なMIMEタイプStringの配列か、MediaType定数値のカンマ区切りリストです。たとえば、
@Consumes({"text/plain,text/html"})
これは以下と同等です。
@Consumes({MediaType.TEXT_PLAIN,MediaType.TEXT_HTML})
以下の例はクラスとメソッド両方に@Consumesを適用する方法を示しています。
@Path("/myResource") @Consumes("multipart/related") public class SomeResource { @POST public String doPost(MimeMultipart mimeMultipartData) { ... } @POST @Consumes("application/x-www-form-urlencoded") public String doPost2(FormURLEncodedProperties formData) { ... } }
doPostメソッドはクラスレベルの@ConsumesのMIMEメディアタイプがデフォルトになります。doPost2メソッドはクラスレベルの@Consumesをオーバーライドし、URLエンコードフォームデータを受け入れ可能にしています。
リクエストされたMIMEタイプに応答可能なリソースメソッドが存在しない場合、HTTP 415 ("Unsupported Media Type") エラーがクライアントに返されます。
この章で以前説明したHelloWorldサンプルは、以下のように、@Consumesを使用してメッセージを設定可能なように修正できます。
@POST @Consumes("text/html") public void postHtml(String message) { // Store the message }
この例では、Javaのメソッドはtext/plain*1MIMEメディアタイプの表現を受け入れます。リソースメソッドはvoidを返す点に注意してください。これは、返される表現は無いことと、HTTP 204 ("No Content")のステータスコードでレスポンスが返されること、を意味します。
29.2.6 Extracting Request Parameters
リクエストメソッドのパラメータにはリクエストから情報を抽出するためにアノテーションを付与することができます。以前の例では、@Pathで宣言したパスにマッチするリクエストURLのパスコンポーネントから、パスパラメータを抽出するために@PathParamの使用方法を示しました。
リソースクラスでは以下の種類のパラメータを抽出可能です。
クエリパラメータ(Query parameters)は、メソッドのパラメータ引数にjavax.ws.rs.QueryParamを設定し、リクエストURIのクエリパラメータから抽出するものです。以下のサンプルは@QueryParamを使用してリクエストURLのQueryコンポーネントからクエリパラメータを抽出しています。
@Path("smooth") @GET public Response smooth( @DefaultValue("2") @QueryParam("step") int step, @DefaultValue("true") @QueryParam("min-m") boolean hasMin, @DefaultValue("true") @QueryParam("max-m") boolean hasMax, @DefaultValue("true") @QueryParam("last-m") boolean hasLast, @DefaultValue("blue") @QueryParam("min-color") ColorParam minColor, @DefaultValue("green") @QueryParam("max-color") ColorParam maxColor, @DefaultValue("red") @QueryParam("last-color") ColorParam lastColor ) { ... }
クエリパラメータstepはリクエストURIのクエリコンポーネント上に存在し、stepの値は抽出されると32bit整数としてパースされ、stepメソッド引数に代入されます。もしstepが存在しない場合、@DefaultValueで宣言されているデフォルト値の2がstepメソッド引数に代入されます。stepの値が32bit引数としてパース出来ない場合、HTTP 400 ("Client Error")レスポンスが返されます。
ユーザ定義型をクエリパラメータとして使用可能です。以下のコードは上の例のクエリパラメータで使用されているColorParamクラスです。
public class ColorParam extends Color { public ColorParam(String s) { super(getRGB(s)); } private static int getRGB(String s) { if (s.charAt(0) == '#') { try { Color c = Color.decode("0x" + s.substring(1)); return c.getRGB(); } catch (NumberFormatException e) { throw new WebApplicationException(400); } } else { try { Field f = Color.class.getField(s); return ((Color)f.get(null)).getRGB(); } catch (Exception e) { throw new WebApplicationException(400); } } } }
ColorParamのコンストラクタは一つのString引数を取ります。
@QueryParamと@PathParamの両方とも、以下のJavaクラスのみ使用可能です。
charを除くすべてのプリミティブ型。Characterを除くプリミティブ型のラッパークラス。- 単一の
String引数をコンストラクタに取る任意のクラス。 - 単一の
String引数のvalueOf(String)という名前のstaticメソッドを持つ任意のクラス。 - Tが既存の条件と一致する
List<T>,Set<T>,SortedSet<T>。パラメータは同一名称で一つ以上の値を持つことがあるため、そのような場合に、すべての値を取得するために使用します。
もし、@DefaultValueが@QueryParamと組み合わされておらずクエリパラメータがリクエストに存在しない場合、値は、List, Set, SortedSetの場合には空のコレクション、オブジェクト型の場合はnull、プリミティブ型はそのデフォルト値、になります。
URIパスパラメータ(URI path parameters)はリクエストURIから抽出され、@Pathクラスレベルアノテーションで指定されるURIパステンプレート変数名とパラメータ名が対応します。URIパラメータはメソッド引数にjavax.ws.rs.PathParamを指定します。以下の例は@Path変数と@PathParamの使用方法を示しています。
@Path("/{username}") public class MyResourceBean { ... @GET public String printUsername(@PathParam("username") String userId) { ... } }
このコードでは、URIパステンプレートの変数名usernameはprintUsernameメソッドの引数として指定されています。@PathParamには変数名usernameが設定されています。実行時には、printUsernameが呼ばれる前に、usernameの値がURIから抽出されてStringにキャストされます。その結果のStringはuserId変数としてメソッドで利用可能になります。
URIパステンプレートの変数は指定の型にキャストできない場合、JAX-RSランタイムはHTTP 400 ("Bad Request")エラーをクライアントに返します。@PathParamが指定の型にキャストできない場合、JAX-RSランタイムはHTTP 404 ("Not Found")エラーをクライアントに返します。
@PathParamパラメータと他のパラメータベースのアノテーション(@MatrixParam, @HeaderParam, @CookieParam, @FormParam)も@QueryParamと同様のルールに従います。
Cookieパラメータ(Cookie parameters)は、javax.ws.rs.CookieParamをパラメータに付与し、cookie関連のHTTPヘッダーで宣言されているcookieから情報を抽出します。ヘッダーパラメータ(Header parameters)は、javax.ws.rs.HeaderParamをパラメータに付与し、HTTPヘッダーから情報を抽出します。Matrix parametersは、javax.ws.rs.MatrixParamをパラメータに付与し、URLパスセグメントから情報を抽出します。
フォームパラメータ(Form parameters)は、javax.ws.rs.FormParamをパラメータに付与し、HTMLフォームに指定されたエンコーディングに http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 で説明される内容に従い、MIMEメディアタイプapplication/x-www-form-urlencodedのリクエスト表現から情報を抽出します。このパラメータはHTMLフォームのPOSTから情報を抽出するのに役立ちます。
以下の例はデータのPOSTのパラメータからnameを抽出しています。
@POST @Consumes("application/x-www-form-urlencoded") public void post(@FormParam("name") String name) { // Store the message }
クエリとパスパラメータの、パラメータ名と値のマッピングを取得するには、以下のコードを使用します。
@GET public String get(@Context UriInfo ui) { MultivaluedMap<String, String> queryParams = ui.getQueryParameters(); MultivaluedMap<String, String> pathParams = ui.getPathParameters(); }
以下のメソッドは、ヘッダーとcookieパラメータの名前と値を抽出してmapで取得します。
@GET public String get(@Context HttpHeaders hh) { MultivaluedMap<String, String> headerParams = hh.getRequestHeaders(); Map<String, Cookie> pathParams = hh.getCookies(); }
通常、@Contextはリクエストやレスポンスに関連するcontextual Javaクラスを取得するために使用します。
フォームパラメータでは、以下のようなコードを書くことが出来ます。
@POST @Consumes("application/x-www-form-urlencoded") public void post(MultivaluedMap<String, String> formParams) { // Store the message }
29.2.7 Configuring JAX-RS Applications
JAX-RSアプリケーションは、WARファイル内にパッケージされている、少なくとも一つのリソースクラスから構成されます。アプリケーションリソースのベースとなりリクエストに応答するためのURIは二種類の方法のうち一つが使用可能です。
- WAR内に
javax.ws.rs.core.Applicationのサブクラスに@ApplicationPathを使用してパッケージ化する。 - デプロイメント記述子
web.xmlにservlet-mappingタグを使用する。
29.2.7.1 Configuring a JAX-RS Application Using a Subclass of Application
ベースURIなど、リソースクラスで定義されるRESTリソースが実行される環境をマニュアル設定するには、javax.ws.rs.core.Applicationのサブクラスを作成します。ベースURIを設定するにはクラスレベルの@ApplicationPathを追加します。
@ApplicationPath("/webapi") public class MyApplication extends Application { ... }
以前の例では、ベースURIに/webapiを設定しており、アプリケーション内で定義されるすべてのリソースは/webapiに関連付くいう意味になります。
デフォルトでは、アーカイブの全リソースがリソースとして処理されます。JAX-RSランタイムでアプリケーションのリソースクラスをマニュアルで登録するにはgetClassesメソッドをオーバーライドします。
@Override public Set<Class<?>> getClasses() { final Set<Class<?>> classes = new HashSet<>(); // register root resource classes.add(MyResource.class); return classes; }
29.2.7.2 Configuring the Base URI in web.xml
JAX-RSアプリケーションのベースURIをweb.xmlデプロイメント記述子のservlet-mappingタグに、servletとしてのApplicationクラスを使用して、設定することができます。
<servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/webapi/*</url-pattern> </servlet-mapping>
なお、この設定はApplicationのサブクラスを使用すると@ApplicationPathで設定されたパスをオーバーライドします。
<servlet-mapping> <servlet-name>com.example.rest.MyApplication</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
29.3 Example Applications for JAX-RS
このセクションではJAX-RSアプリケーションの作成・デプロイ・実行方法を紹介します。JAX-RSアノテーションを使用するシンプルなwebアプリケーションの作成・ビルド・デプロイ・テストに要求される手順を示します。
29.3.1 Creating a Simple RESTful Web Service
このセクションではMavenアーキタイプを使用してRESTful webサービスを作成するためにNetBeasn IDEを使用する方法を紹介します。アーキタイプはアプリケーションのスケルトンを生成するので、それから適切なメソッドを実装します。
アプリケーションのソースはtut-install/examples/jaxrs/hello/から参照できます*2。
29.3.1.1 To Create a RESTful Web Service Using NetBeans IDE
- Installing the Tutorial Archetypesに記載されているチュートリアルのアーキタイプをインストール済みなことを確認する。
- NetBeans IDEで、
jaxrs-service-archetypeMavenアーキタイプを使用してsimple web applicationを生成する。このアーキタイプはシンプルな"Hello, World" webアプリケーションを生成します。 HelloWorld.javaファイルのgetHtml()を書き換えます。//TODOコメントを以下のテキストに置き換えます。
@GET @Produces("text/html") public String getHtml() { return "<html lang=\"en\"><body><h1>Hello, World!!</body></h1></html>"; }
Note: 生成されるMIMEタイプはHTMLなので、戻り値にはHTMLタグを使用可能です。
4. ProjectsペインのHelloWorldApplicationプロジェクトを右クリックしてRunを選択します。
これはビルドを行いアプリケーションをGlassFish Serverにデプロイします。
5. ブラウザで以下のURLを開きます。
http://localhost:8080/HelloWorldApplication/HelloWorldApplication
ブラウザが開かれるとHello, World!!が表示されます。
NetBeans IDEを使用するJAX-RSアプリケーションのデプロイと実行を紹介する他のサンプルアプリケーションは、The rsvp Example Applicationと http://docs.oracle.com/javaee/7/firstcup/doc/ のYour First Cup: An Introduction to the Java EE Platformを参照してください。また、NetBeans IDEチュートリアルサイトの https://netbeans.org/kb/docs/websvc/rest.html の"Getting Started with RESTful Web Services"などにもチュートリアルがあります。このチュートリアルにはデータベースを使用するCRUDアプリケーションを作成するセクションが含まれています。CRUDは永続化ストレージと関係データベースの基本的な4つの機能です。
29.3.2 The rsvp Example Application
rsvpサンプルアプリケーションはtut-install/examples/jaxrs/rsvp/*3ディレクトリにあり、このアプリケーションはイベントに出席するかどうかを表明するものです。イベントに招待された人とその返答はJPAを使用してJava DBに格納されます。rsvpのJAX-RSリソースはステートレスセッションビーンで公開されます。
29.3.2.1 Components of the rsvp Example Application
rsvpには三つのEJBrsvp.ejb.ConfigBean, rsvp.ejb.StatusBean, rsvp.ejb.ResponseBeanがあります。
ConfigBeanはデータベースのデータを初期化するシングルトンセッションビーンです。
StatusBeanはイベントの全招待者の現在のステータスを表示するJAX-RSリソースを公開します。URIパステンプレートがまずクラスに、それからgetEventメソッドに宣言されています。
@Stateless @Named @Path("/status") public class StatusBean { ... @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Path("{eventId}/") public Event getEvent(@PathParam("eventId") Long eventId) { ...
二つの@Pathアノテーションを結合して以下のURIパステンプレートになります。
@Path("/status/{eventId}/")
@GETを付与されたgetEventメソッドはHTTP GETリクエストに応答し、URIパス変数eventIdは@PathParam変数になります。eventId変数は個々のイベントのデータベース上の現在のすべての返答を取得するために使用します。
ResponseBeanは個々のイベントの招待の返答を設定するためのJAX-RSリソースを公開します。ResponseBeanのURIパステンプレートは以下のように宣言されています。
@Path("/{eventId}/{inviteId}")
URIパス変数はパステンプレートでeventIdとinviteIdとして宣言されています。StatusBeanでは、eventIdは個々のイベントを示すユニークIDとして使われます。個々のイベント招待はユニークIDinviteIdを持ちます。これらのパス変数は二つのJAX-RSメソッドResponseBeanのgetResponseとputResponseで使用されます。getResponseメソッドはHTTP GETリクエストに応答するためのメソッドで、招待者の現在の返答状況と返答を変更するためのフォームを表示します。
javaeetutorial.rsvp.rest.RsvpApplicationクラスはクラスレベルにjavax.ws.rs.ApplicationPathを適用してリソースのルートアプリケーションパスを定義します。
@ApplicationPath("/webapi") public class RsvpApplication extends Application { }
自身の出欠を変更したい招待者はフォームデータをサブミットすると、putResponseメソッドがHTTP POSTリクエストを処理します。変更後の出欠はHTTP POSTから抽出されてuserResponse文字列に格納されます。putResponseメソッドはデータベースの招待者の出欠を更新するためにuserResponse, eventId, inviteIdを使用します。
rsvpのイベント・招待者・出欠はJPAのエンティテイとして表現されています。rsvp.entity.Event, rsvp.entity.Person, rsvp.entity.Responseエンティティがそれぞれ、イベント・招待者・出欠、に対応します。
rsvp.util.ResponseEnumクラスは招待者が取りうるすべての出欠ステータスを表現するenum型です。
また、このwebアプリケーションはマネジードビーンStatusManagerとEventManagerを含み、これはStatusBeanとResponseBeanで公開されるリソースを呼ぶためにJAX-RS Client APIを使用します。 rsvpで使用されているClient APIの詳細については、"The Client API in the rsvp Example Application"を参照してください。
29.3.2.2 Running the rsvp Example Application
NetBeans IDEとMavenどちらでもrsvpサンプルアプリケーションのデプロイと実行が可能です。
NetBeans IDEを使用したrsvpサンプルアプリケーションの起動方法
- データベースサーバが起動していなければ、Starting and Stopping the Java DB Serverの手順に従って起動します。
- GlassFish Serverが起動していることを確認します(Starting and Stopping GlassFish Server)。
- Fileメニューから、Open Projectを選択します。
- Open Projectダイアログボックスで以下を選びます。
tut-install/examples/jaxrs rsvpフォルダーを選択します。- Open Projectをクリックします。
- Projectsタブで、
rsvpプロジェクトを右クリックしてRunを選択します。
プロジェクトがコンパイルされ、アセンブリされ、GlassFish Serverにデプロイされます。webブラウザで以下のURLを開きます。
http://localhost:8080/rsvp/index.xhtml - webブラウザでDuke's BirthdayイベントのEvent statusリンクをクリックします。
現在の招待者と出欠状況が確認できます。 - 表のStatusカラムの招待者の現在の出欠をクリックし、出欠を変更し、Update your statusをクリックします。
招待者の新しい出欠状況が表に表示されます。
Mavenを使用したrsvpサンプルアプリケーションの起動方法
- データベースサーバが起動していなければ、Starting and Stopping the Java DB Serverの手順に従って起動します。
- GlassFish Serverが起動していることを確認します(Starting and Stopping GlassFish Server)。
- ターミナルで以下のディレクトリに移動します。
tut-install/examples/jaxrs/rsvp/ - 以下のコマンドを実行します。
mvn install
このコマンドは、ビルド・アセンブル・GlassFish Serverへのrsvpのデプロイを行います。 - 以下のURLでブラウザを開きます。
http://localhost:8080/rsvp/ - webブラウザでDuke's BirthdayイベントのEvent statusリンクをクリックします。
現在の招待者と出欠状況が確認できます。 - 表のStatusカラムの招待者の現在の出欠をクリックし、出欠を変更し、Update your statusをクリックします。
招待者の新しい出欠状況が表に表示されます。
29.3.3 Real-World Examples
たいてのブログサイトはRESTful webサービスを使用しています。そうしたサイトは、他のリソースへのリンクを含むRSSやAtom形式のXMLファイルをダウンロードが可能です。RESTライクの開発者向けインタフェースを使用する他のwebサイトやwebアプリケーションにはTwitterやAmazon S3などがあります。Amazon S3では、REST風なHTTPインターフェースもしくはSOAPインタフェースのどちらかを使用して、バケットやオブジェクトの作成・一覧取得・検索が可能です。JerseyのサンプルにはRESTfulインタフェースのストレージサービスが含まれています。
29.4 Further Information about JAX-RS
RESTful webサービスとJAX-RSのより詳細な情報については以下を参照してください。
- "Fielding Dissertation: Chapter 5: Representational State Transfer (REST)":
http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm - RESTful Web Services, by Leonard Richardson and Sam Ruby, available from O'Reilly Media at
http://shop.oreilly.com/product/9780596529260.do - JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services:
http://jcp.org/en/jsr/detail?id=339 - Jersey project:
https://jersey.java.net/
関連リンク
- The Java EE 7 Tutorialのテキトー翻訳まとめ - Qiita - Java EE 7 Tutorialのうち、自分がテキトー翻訳したものの一覧