先ほどのPOSTメソッドの例では、リクエストからのデータをHTMLのFORM形式としていました。しかし、AjaxリクエストではJSONのようなJavaScriptと親和性のあるデータ形式を使うのが一般的です。JAX-RSでは、出力データの場合と同様に、入力データにもJSONやXMLなどが使えます。
サンプルとして、前回の記事でJSFを使って作成した認証処理をJAX-RSで実装してみましょう。まず、リクエストのJSONデータを示すJavaクラスを定義します。このLoginRequestクラスは、JSONデータに対応し、ユーザーIDとパスワードを持ちます。
public class LoginRequest { @NotNull @Size(min = 1) public String id; @NotNull @Size(min = 1) public String password; }
続いて、ログイン処理を行うloginメソッドを定義します。
@Path("login") @RequestScoped public class LoginResource { @POST @Consumes(MediaType.APPLICATION_JSON) // JSONでデータを受け取る @Produces(MediaType.APPLICATION_JSON) public LoginResponse login(@Valid LoginRequest loginReq) { // 省略 } }
このメソッドには、@Consumesアノテーションで「Application/json」形式のデータを受け取ることを指定し、引数にはJSONデータを示すLoingRequestクラスを指定します。
loginメソッドを実行するには、「{“id”:”xxx”, “password”:”xxxx”}」のようなJSONデータをリクエストに指定します。
このように、JSONなどのデータからJavaオブジェクトの変換をJAX-RSが行いますので、さまざまなデータの扱いが簡単になります。
JSFと同様に、JAX-RSでもBeanValidationを用いた入力値のチェックが可能です。BeanValidationのアノテーションをリソースメソッドの引数に指定すると、リソースメソッドの実行前にチェックが行われます。
まず、String型やプリミティブ型の引数に対するアノテーションの指定方法を説明します。
リスト2-3でString型のIDを指定して検索を行うリソースメソッドを紹介しました。ここで、IDの入力を必須としたい場合、以下のように@NotNullアノテーションを引数に追加します。
@GET @Path("{id}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Employee get(@PathParam("id") @NotNull long id) {
次にJavaオブジェクトに対するアノテーションの指定方法を説明します。
前述のJSONデータの受け取る方法では、リソースメソッドの引数にLoginRequestクラスのようなJavaオブジェクトを指定していました。Javaオブジェクトに対するチェックは、引数に @Valid アノテーションを追加し、Javaオブジェクトの各フィールドにBeanValidationのチェック用のアノテーションを追加します。
リスト3とリスト4のソースコードは、この内容に沿ってアノテーションを設定していますので、確認してみてください。
バリデーションエラーは、何もしなければシステムエラーとして扱われます。バリデーションエラーで発生するConstraintViolationExceptionをハンドリングするために、ExceptionMapperという例外に対する拡張ポイントを作成します。
コードは以下のようになります。
@Provider //【1】 public class BeanValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> { //【2】例外からResponseへの変換 @Override @Produces(MediaType.APPLICATION_JSON) public Response toResponse(ConstraintViolationException exception) { List<Pair> list = exception.getConstraintViolations().stream() .map(v -> new Pair(last(v.getPropertyPath().toString()), v.getMessage())) .collect(Collectors.toList()); //【3】Responseの構築 return Response.status(400).type(MediaType.APPLICATION_JSON) .entity(new GenericEntity<List<Pair>>(list){}) .build(); } private String last(String str) { System.out.println(str); String[] arr = str.split("[.]"); return arr[arr.length-1]; } }
【1】では、クラス宣言の前に、JAX-RSに処理を追加することを示す@Provider アノテーションを指定して、JAX-RSの拡張であることを示しています。
【2】では、toResponseメソッドを宣言し、例外からResponseへの変換コードを書きます。
Responseとは、JAX-RSのレスポンスを表す汎用的なクラスです。実際には、リソースクラスのメソッドの戻り値は最終的にResponseに変換されます。
【3】では、Responseを構築しています。例外から入力チェックエラーのメッセージを取得して、JSON形式のデータをレスポンスに書き込んでいます。また、ステータスコードにHTTPレスポンスコードの「400」(BadRequest)も設定しています。
RESTではクライアントプログラムが、サーバからの処理結果を正しく判定するために、処理結果に応じたステータスコードを設定することが重要です。
ここまで、JAX-RSの基本的なプログラムの作成方法を解説しました。最後に、JAX-RSを用いてWebアプリケーションを作成する方法について解説します。
ここまでの解説では、Viewに関する説明をしてきませんでした。実は、JAX-RSはWebサービスを実現するための仕様のため、Viewに関する特定の機能を提供していません。そこで、JAX-RSでWebアプリケーションを作成する2つの方法を紹介します。
1つ目の方法は、JAX-RSではViewの構築に必要なデータだけを提供し、Viewの構築はクライアントに任せる方式です。近年、クライアント側にはさまざまな技術が使われるようになっています。例えば、JavaScriptによるクライアントサイドのフレームワークや、モバイル向けのHTMLサイト、iOSやAndroidなどのネイティブアプリなどです。
さまざまなクライアントから利用するシステムでは、サーバ側はREST APIを公開するだけのシンプルな構成にしておくことで、クライアントの種類に応じた技術を採用できるようになります。
REST APIを使うと、クライアントとサーバはREST APIを通してデータを送受信するようになるので、両者で使用する技術を変更しても影響を受けにくくなります。
クライアントがブラウザのみの場合、サーバ側でViewを構築することもできます。
具体的な方法として、JAX-RSの参照実装フレームワークである「Jersey」の提供するMVC拡張機能を使う方法があります。このMVC拡張機能を使うことで、JAX-RSのリソースのメソッドの戻り値に、JSPなどのテンプレートを指定できるようになります。
GETリクエストに対する例を示します。
@GET @Template(name = "/welcome") public Model notifications() { Model model = new Model(); model.put("notifications", Arrays.asList("This isJAX-RS app", "note..")); return model; }
MVC拡張機能を利用した場合、リソースクラスのメソッドはレスポンスとしてViewの内容を返却する必要があります。具体的には、Viewで使うテンプレートとテンプレートに埋め込むデータの2つが必要です。上記のサンプルコードでは、@Templateアノテーションで、遷移先のViewテンプレートのパスを設定しています。戻り値のModelがテンプレートに埋め込むデータです。
こうすることで、レスポンスにはwelcome.jspとModelから生成されたHTMLが設定されるようになります。
このように、MVC拡張機能を用いると、JAX-RSをシンプルなWebアプリケーションフレームワークとして使えます。
まだ正式にリリースはされていませんが、次期のJava EE 8には、公式のアクションベースのフレームワーク「MVC 1.0」が含まれる予定です。
「MVC 1.0」はJAX-RSに、JSPやFaceletsなどのテンプレート機能とCDI、BeanValidationを盛り込んだ、前述のMVC拡張と似たフレームワークとなることがアナウンスされています。
MVC 1.0が登場すると、JAX-RSだけで、RESTベース、ブラウザベース両方のシステム構築が可能となるため、筆者はJava EE 8の目玉になると予想しています。
今回は、RESTの概要、JAX-RSの基本的な使い方、JAX-RSを用いたViewの構築について解説しました。
前回の記事で紹介したJSFとJAX-RSはどちらもJava EEの標準仕様です。最後にこれらの使い分けについて述べます。
JSFは、Viewが主役のコンポーネントベースのフレームワークであり、ブラウザベースのアプリケーションを作成することに特化しています。また、セッションを多用するステートフルなフレームワークです。このため、View間で保持する情報が多数ある場合や、Viewの共通部分が多い場合には、ステートフルな性質やコンポーネントの再利用性を生かして効率的な開発を行えます。
一方、JAX-RSは、URIとHTTPメソッドによって処理を決定し、レスポンスデータを返却するシンプルなアクションベースのフレームワークです。サーバサイドはREST APIを提供するシンプルな構成になるため、ブラウザに限らず多種多様なクライアントに対応できます。
また、セッションを用いない、ステートレスなフレームワークのため、単体ではViewに関する機能を持たず、Viewを構築する技術やクライアントの状態管理は別途考慮しなければいけません。そのため、さまざまなViewを提供する必要があるシステムやViewの変更が多いシステムに適しています。
また、ステートレスな性質からサーバ負荷が軽くなり負荷分散も容易になるため、アクセスが多いシステムにも適しています。JSFと比較するとJAX-RSはシンプルで使いやすく、データ変換やバリデーションのような定型的なコードをほとんど書く必要がないため、効率的に開発できるフレームワークです。
Java EE標準の中で、アクションベースでWebアプリケーションを構築する場合には有力な選択肢になります。
次回は、Javaの代表的なオープンソースソフトウェアフレームワークであるSpring Frameworkについて、Spring BootとSpring MVCを中心に解説します。
なお、今回作成したアプリケーションのソースコードは筆者のGitHubで公開しています。
本記事は重要なトピックのみを抜粋して紹介しましたので、ソースコード全体については以下のGitHubリポジトリを参照してください。
ウルシステムズ シニアコンサルタント
金融系を中心に多数のシステム設計、構築を担当してきた。現在は顧客企業の若手社員を対象とした技術指導や、システム構築プロジェクトの技術支援を担当している。
「Enterprise Geeks」ではJava EEの記事を執筆中。
ここ最近の興味は、ドメイン駆動設計と関数型プログラミング。
Copyright © ITmedia, Inc. All Rights Reserved.