Spring 3 MVC の HttpMessageConverter 機能を使って RESTful な Web サービスを作成する
はじめに
この記事と似たテーマを取り上げた「Spring 3 を使って RESTful な Web サービスを作成する」(「参考文献」を参照) でも、Spring を使って RESTful な Web サービスを作成する方法を紹介しました。またその記事では、ContentNegotiatingViewResolver
を使って多様な表現を作成する方法を説明しました。多様な表現を作成できることは、RESTful な Web サービスの重要な機能の 1 つです。この記事では、多様な表現を作成するための方法として、HttpMessageConverter
を使用する方法を説明します。この記事で紹介するサンプル・コードでは、HttpMessageConverter
と RestTemplate
とを組み合わせてサービスと通信する方法を示します。
Spring MVC での REST のサポート
このセクションでは、Spring の重要な機能、つまり RESTful な Web サービスをサポートするアノテーションの概要を説明します。
@Controller
@Controller
アノテーションを使用すると、MVC のコントローラーとなるクラスにアノテーションを付け、HTTP リクエストを処理することができます。@RequestMapping
@RequestMapping
アノテーションを使用すると、特定の HTTP メソッド、URI、HTTP ヘッダーを処理する関数にアノテーションを付けることができます。このアノテーションは Spring の REST サポートにとって非常に重要です。@RequestMapping アノテーションのmethod
パラメーターを変更すると、他の HTTP メソッドを処理することができます。例えば以下のようにします。
@RequestMapping(method=RequestMethod.GET, value="/emps", headers="Accept=application/xml, application/json")
@PathVariable
@PathVariable
アノテーションを使用すると、URI のパス変数をパラメーターとして注入することができます。例えば以下のようにします。
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}") public ModelAndView getEmployee(@PathVariable String id) { … }
- 他の便利なアノテーション
@RequestParam
を使用すると、URL パラメーターをメソッドの中に注入することができます。@RequestHeader
を使用すると、特定の HTTP ヘッダーをメソッドの中に注入することができます。@RequestBody
を使用すると、HTTP リクエストの本体をメソッドの中に注入することができます。@ResponseBody
を使用すると、コンテンツまたはオブジェクトを HTTP レスポンスの本体として返すことができます。HttpEntity<T>
をパラメーターとして使用すると、メソッドの中に HttpEntity<T> を自動的に注入することができます。ResponseEntity<T>
を使用すると、カスタムのステータスまたはヘッダーを持つ HTTP レスポンスを返すことができます例えば以下のようにします。
public @ResponseBody Employee getEmployeeBy(@RequestParam("name") String name, @RequestHeader("Accept") String accept, @RequestBody String body) {…} public ResponseEntity<String> method(HttpEntity<String> entity) {…}
メソッドに注入できるアノテーションやオブジェクトとしてサポートされているものの完全な一覧は、Spring のドキュメントを参照してください (「参考文献」を参照)。
多様な表現のサポート
同じリソースを多様な MIME タイプで表現できることは、RESTful な Web サービスの重要な側面の 1 つです。通常、異なる表現によるリソースを取得するために、HTTP ヘッダーの Accept の値のみが異なる、同じ URI を使用します。また、異なる URI を使用することも、異なるリクエスト・パラメーターを持つ URI を使用することもできます。
「Spring 3 を使って RESTful な Web サービスを作成する」(「参考文献」を参照) では ContentNegotiatingViewResolver
を紹介しました。ContentNegotiatingViewResolver
を使って異なるビュー・リゾルバーを選択することで、(Accept ヘッダーの異なる) 同じ URI を扱うことができます。つまり ContentNegotiatingViewResolver を使用することで、多様な表現を作成することができます。
また、多様な表現を作成するための別の方法として、HttpMessageConverter
と @ResponseBody
アノテーションとを組み合わせる方法があります。この方法を使う場合には、ビュー技術を使う必要はありません。
HttpMessageConverter
HTTP のリクエストとレスポンスはテキスト・ベースです。つまりブラウザーとサーバーはそのままのテキストを交換して通信します。しかし Spring では、コントローラー・クラスのメソッドは純粋な String 型と、ドメイン・モデル (または他の Java 埋め込みオブジェクト) を返します。Spring では、オブジェクトとそのままのテキストとの間で、どのようにしてシリアライズまたはデシリアライズするのでしょう。その処理を行うのが HttpMessageConverter
です。Spring には、一般的な用途に合った実装がバンドルされています。表 1 はその一例です。
表 1. HttpMessageConverter の例
実装の種類 | 機能 |
---|---|
StringHttpMessageConverter | リクエストとレスポンスのストリングを読み書きします。デフォルトで、メディア・タイプ text/* をサポートし、text/plain という Content-Type で書き込みます。 |
FormHttpMessageConverter | リクエストとレスポンスのデータを読み書きします。デフォルトで、メディア・タイプ application/x-www-form-urlencoded を読み取り、MultiValueMap<String,String> にデータを書き込みます。 |
MarshallingHttpMessageConverter | Spring のマーシャラーとアンマーシャラーを使って XML データを読み書きします。メディア・タイプ application/xml のデータを変換します。 |
MappingJacksonHttpMessageConverter | Jackson の ObjectMapper を使って JSON データを読み書きします。メディア・タイプ application/json のデータを変換します。 |
AtomFeedHttpMessageConverter | ROME の Feed API を使って Atom フィードを読み書きします。メディア・タイプ application/atom+xml のデータを変換します。 |
RssChannelHttpMessageConverter | ROME の Feed API を使って RSS フィードを読み書きします。メディア・タイプ application/rss+xml のデータを変換します。 |
RESTful な Web サービスを作成する
このセクションでは、多様な表現を生成できる、単純で RESTful な Web サービスを作成する方法を学びます。このサンプル・アプリケーションで使用するリソースの一部は「Spring 3 を使って RESTful な Web サービスを作成する」(「参考文献」を参照) の中で作成したものです。このアプリケーションのサンプル・コードはダウンロードすることもできます。
まず、HttpMessageConverter
を構成する必要があります。多様な表現を生成するためには、いくつかの HttpMessageConverter
インスタンスをカスタマイズし、多様なメディア・タイプにオブジェクトを変換します。このセクションでは、メディア・タイプが JSON、Atom、XML の場合について説明します。
JSON
まず、最も単純な例から始めましょう。JSON は人間が容易に読み書きできる軽量なデータ交換フォーマットです。リスト 1 は JSON コンバーターを構成するためのコードを示しています。
リスト 1. rest-servlet.xml で HttpMessageConverter を構成する
<bean class="org.springframework.web.servlet.mvc.annotation .AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="jsonConverter" /> <ref bean="marshallingConverter" /> <ref bean="atomConverter" /> </list> </property> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json .MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json" /> </bean>
この構成の中では、3 つのコンバーターが登録されています。MappingJacksonHttpMessageConverter
は、オブジェクトから JSON への変換と、その逆変換に使われています。この組み込みのコンバーターは Jackson の ObjectMapper
を使って JSON を JavaBean にマッピングします。そのため、以下の Jackson JAR ファイルをクラスパスに追加する必要があります。
- org.codehaus.jackson.jar
- org.codehaus.jackson.mapper.jar
次のステップでは、JSON 表現を要求するリクエストを扱うメソッドを作成します。リスト 2 にその詳細を示します。
リスト 2. EmployeeController で定義された JSON リクエストを扱う
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}", headers="Accept=application/json") public @ResponseBody Employee getEmp(@PathVariable String id) { Employee e = employeeDS.get(Long.parseLong(id)); return e; } @RequestMapping(method=RequestMethod.GET, value="/emps", headers="Accept=application/json") public @ResponseBody EmployeeListinggetAllEmp() { List<Employee> employees = employeeDS.getAll(); EmployeeListinglist = new EmployeeList(employees); return list; }
@ResponseBody
アノテーションを使用することで、返されるオブジェクト (Employee
または EmployeeList
) をレスポンス本体に含めるようにし、そのレスポンス本体の内容を MappingJacksonHttpMessageConverter
によって JSON にマッピングしています。
HttpMessageConverter
と @ResponseBody
を使用すると、Spring のビュー技術を使用せずに多様な表現を実装することができ、この点は ContentNegotiatingViewResolver
を使用する方法よりも優れています。
これで、curl、または RESTClient というFirefox プラグインを使ってリクエストを呼び出すことができます。HTTP ヘッダーとして Accept=application/json
を追加するのを忘れないでください。リスト 3 は、想定されるレスポンスを JSON フォーマットで表現したものです。
リスト 3. getEmp() と getAllEmp() の結果の JSON 表現
Response for /rest/service/emp/1 {"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"} Response for /rest/service/emps {"count":2, "employees":[ {"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"}, {"id":2,"name":"Wu Dong Fei","email":"wudongf@cn.ibm.com"} ]}
XML
Spring に組み込まれたコンバーター、MarshallingHttpMessageConverter
は、オブジェクトと XML との間のマッピング (OXM) に使用されます。下記の例では MarshallingHttpMessageConverter のマーシャラーとアンマーシャラーとして JAXB 2 を使用しています。リスト 4 にこの構成を示します。
リスト 4. MarshallingHttpMessageConverter を構成する
<bean id="marshallingConverter" class="org.springframework.http.converter.xml .MarshallingHttpMessageConverter"> <constructor-arg ref="jaxbMarshaller" /> <property name="supportedMediaTypes" value="application/xml"/> </bean> <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="classesToBeBound"> <list> <value>dw.spring3.rest.bean.Employee</value> <value>dw.spring3.rest.bean.EmployeeList</value> </list> </property> </bean>
JAXB 2 では java.util.List<T> から XML へのマッピングのサポートが十分なものではないことを理解しておくことが重要です。一般的な方法としては、オブジェクトの集合に対するラッパー・クラスを追加します。この、JAXB でアノテーションが付けられたクラスの詳細については、「Spring 3 を使って RESTful な Web サービスを作成する」(「参考文献」を参照) を参照するか、あるいはソース・コードをダウンロードしてください。
コントローラーの中でリクエストを処理するメソッドについてはどうなのでしょう。前に戻ってリスト 2 のコードを見てください。当然ですが、ここには何もコードを追加する必要がありません。単純に、サポートされているもう 1 つのメディア・タイプを下記のように Accept
ヘッダーに追加すればよいのです。
headers=”Accept=application/json, application/xml”
MarshallingHttpMessageConverter により、オブジェクトは要求されたタイプ (JSON または XML) に適切にマッピングされます。リスト 5 は application/xml 表現を要求した場合に想定される結果を示しています。
リスト 5. getEmp() と getAllEmp() の結果の XML 表現
Response for /rest/service/emp/1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <employee> <email>huangyim@cn.ibm.com</email> <id>1</id> <name>Huang Yi Ming</name> </employee> Response for /rest/service/emps <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <employees> <count>2</count> <employee> <email>huangyim@cn.ibm.com</email> <id>1</id> <name>Huang Yi Ming</name> </employee> <employee> <email>wudongf@cn.ibm.com</email> <id>2</id><name>Wu Dong Fei</name> </employee> </employees>
Atom フィード
RESTful な Web サービスでデータを交換するためのフォーマットとして、Atom フィードもよく使われています。Atom フィード文書は Atom フィードの表現であり、その中には、そのフィードに関するメタデータと、そのフィードに関連付けられた一部のエントリーまたはすべてのエントリーが含まれています。Atom フィードのルートは atom:feed
要素です。また、Atom 出版プロトコル (Atom Publish Protocol: APP) を使って交換フォーマットと動作を定義する方法もあります。(この記事では Atom フォーマットや APP フォーマットの定義については省略します。これらのフォーマットの詳細については「参考文献」を参照してください。)
下記の例では AtomFeedHttpMessageConverter
を使用して Atom フィードを変換しています。この Atom フィードには ROME の Atom API を使用しています。そのため、sun.syndication.jar という JAR ファイルをクラスパスに含める必要があります。リスト 6 に、このコンバーターの構成を示します。
リスト 6. AtomFeedHttpMessageConverter を構成する
<bean id="atomConverter" class="org.springframework.http.converter.feed .AtomFeedHttpMessageConverter"> <property name="supportedMediaTypes" value="application/atom+xml" /> </bean>
リスト 7 に Atom リクエストとフィードの生成を扱うコードを示します。
リスト 7. EmployeeControllerの getEmpFeed() と、AtomUtil クラス
@RequestMapping(method=RequestMethod.GET, value="/emps", headers="Accept=application/atom+xml") public @ResponseBody Feed getEmpFeed() { List<Employee> employees = employeeDS.getAll(); return AtomUtil.employeeFeed(employees, jaxb2Mashaller); } public static Feed employeeFeed( List<Employee> employees, Jaxb2Marshaller marshaller) { Feed feed = new Feed(); feed.setFeedType("atom_1.0"); feed.setTitle("Employee Atom Feed"); List<Entry> entries = new ArrayList<Entry>(); for(Employee e : employees) { StreamResult result = new StreamResult( new ByteArrayOutputStream()); marshaller.marshal(e, result); String xml = result.getOutputStream().toString(); Entry entry = new Entry(); entry.setId(Long.valueOf(e.getId()).toString()); entry.setTitle(e.getName()); Content content = new Content(); content.setType(Content.XML); content.setValue(xml); List<Content> contents = new ArrayList<Content>(); contents.add(content); entry.setContents(contents); entries.add(entry); } feed.setEntries(entries); return feed; }
上記のコードでは以下に注意してください。
getEmpFeed()
メソッドはgetAllEmp()
と同じ URI を扱いますが、Accept
ヘッダーは異なります。employeeFeed()
メソッドを使ってEmployee
オブジェクトを XML にマーシャリングし、その XML をフィードのエントリーの<content>
要素に追加しています。
リスト 8 は /rest/service/emps という URI の application/atom+xml 表現を要求した場合の出力を示しています。
リスト 8. application/atom+xml を要求した場合の /rest/service/emps の出力
<?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>Employee Atom Feed</title> <entry> <title>Huang Yi Ming</title> <id>1</id> <content type="xml"> <employee> <email>huangyim@cn.ibm.com</email> <id>1</id> <name>Huang Yi Ming</name> </employee> </content> </entry> <entry> <title>Wu Dong Fei</title> <id>2</id> <content type="xml"> <employee> <email>wudongf@cn.ibm.com</email> <id>2</id> <name>Wu Dong Fei</name> </employee> </content> </entry> </feed>
POST、PUT、DELETE を実装する
ここまでに示した例では、いくつかのメソッドを実装して HTTP の GET
メソッドの処理を行いました。リスト 9 に、POST
、PUT
、DELETE
メソッドの実装を示します。
リスト 9. EmployeeController の POST、PUT、DELETE メソッド
@RequestMapping(method=RequestMethod.POST, value="/emp") public @ResponseBody Employee addEmp(@RequestBody Employee e) { employeeDS.add(e); return e; } @RequestMapping(method=RequestMethod.PUT, value="/emp/{id}") public @ResponseBody Employee updateEmp( @RequestBody Employee e, @PathVariable String id) { employeeDS.update(e); return e; } @RequestMapping(method=RequestMethod.DELETE, value="/emp/{id}") public @ResponseBody void removeEmp(@PathVariable String id) { employeeDS.remove(Long.parseLong(id)); }
@RequestBody
アノテーションは addEmp()
メソッドと updateEmp()
メソッドに使われています。@RequestBody アノテーションは HTTP リクエストの本体を引数に取り、登録された HttpMessageConverter
を使ってその引数をオブジェクト・クラスに変換します。次のセクションでは、これらのサービスとの通信を、RestTemplate
を使って行います。
RestTemplate を使って REST サービスと通信する
「Spring 3 を使って RESTful な Web サービスを作成する」(「参考文献」を参照) では、curl と RESTClient を使って REST サービスをテストする方法を紹介しました。プログラミング・レベルでは、通常は Jakarta Commons の HttpClient を使って REST サービスをテストします (ただし、それについてはこの記事では省略します)。RestTemplate
という、Spring の REST クライアントを使うこともできます。RestTemplate
は概念的に、JdbcTemplate
や JmsTemplate
といった Spring の他のテンプレート・クラスと似ています。
RestTemplate
も HttpMessageConverter
を使用します。リクエストの中でオブジェクト・クラスを渡すと、コンバーターによってマッピングの処理が行われます。
RestTemplate を構成する
リスト 10 は RestTemplate
の構成を示しています。この構成にも、先ほど紹介した 3 つのコンバーターを使用しています。
リスト 10. RestTemplate を構成する
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <ref bean="marshallingConverter" /> <ref bean="atomConverter" /> <ref bean="jsonConverter" /> </list> </property> </bean>
この記事の例では、サーバーとの通信を単純化するメソッドの一部のみを使用しました。RestTemplate
は他にも以下のようなメソッドをサポートしています。
exchange
: リクエスト本体を使って HTTP メソッドを実行し、レスポンスを取得します。getForObject
: HTTPGET
メソッドを実行し、レスポンスをオブジェクトとして取得します。postForObject
: リクエスト本体を使って HTTPPOST
メソッドを実行します。put
: リクエスト本体を使って HTTPPUT
メソッドを実行します。delete
: URI に対して HTTPDELETE
メソッドを実行します。
コード・サンプル
以下のコード・サンプルは RestTemplate
の使い方を説明しています。ここで使用している API の詳細な説明は RestTemplate の API (「参考文献」を参照) を参照してください。
リスト 11 は、リクエストにヘッダーを追加した後、そのリクエストを呼び出す方法を示しています。MarshallingHttpMessageConverter
を使用すると、レスポンスを取得した後、指定のクラスに変換することができます。異なるメディア・タイプを使用すると、他の表現をテストすることができます。
リスト 11. XML 表現を要求する例
HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_XML); HttpEntity<String> entity = new HttpEntity<String>(headers); ResponseEntity<EmployeeList> response = restTemplate.exchange( "http://localhost:8080/rest/service/emps", HttpMethod.GET, entity, EmployeeList.class); EmployeeListingemployees = response.getBody(); // handle the employees
リスト 12 は新しい従業員 (employee) をサーバーに POST する方法を示しています。サーバー・サイドのサービス addEmp()
は、メディア・タイプが application/xml と application/json のデータを受け付けます。
リスト 12. 新しい従業員を POST する
Employee newEmp = new Employee(99, "guest", "guest@ibm.com"); HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp); ResponseEntity<Employee> response = restTemplate.postForEntity( "http://localhost:8080/rest/service/emp", entity, Employee.class); Employee e = response.getBody(); // handle the employee
リスト 13 は、従業員に対する変更を PUT して、その従業員の元のデータを更新する方法を示しています。またリスト 13 には、リクエスト URI の中でプレース・ホルダー ({id}
) として使用できる機能も示されています。
リスト 13. PUT を使用して従業員のデータを更新する
Employee newEmp = new Employee(99, "guest99", "guest99@ibm.com"); HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp); restTemplate.put( "http://localhost:8080/rest/service/emp/{id}", entity, "99");
リスト 14 は既存の従業員のデータを DELETE する方法を示しています。
リスト14. 既存の従業員のデータを DELETE する
restTemplate.delete( "http://localhost:8080/rest/service/emp/{id}", "99");
まとめ
この記事では、Spring 3 で導入された HttpMessageConverter
について学びました。HttpMessageConverter
を使用することで、クライアント・サイドとサーバー・サイドの両方で、多様な表現をサポートすることができます。この記事で提供しているソース・コードを利用すると、この記事で説明した HttpMessageConverter の実装と、「Spring 3 を使って RESTful な Web サービスを作成する」で説明した ContentNegotiatingViewResolver
を使用した実装との違いを調べることができます。
ダウンロード可能なリソース
- このコンテンツのPDF
- Article source code (src_code.zip | 11KB)
関連トピック
- この記事と合わせて、Spring を使って RESTful な Web サービスを作成する方法を紹介した「Spring 3 を使って RESTful な Web サービスを作成する」(developerWorks、2010年7月) を読んでください。
- REST の紹介、その他関連のリンクをウィキペディアで見てください。
- Spring MVC 3 Showcase を調べ、この技術によって何を実現できるかを理解してください。このサイトには、サンプル・プロジェクトを始め、この技術を解説したスライド・プレゼンテーションやスクリーンキャストも用意されています。
- Spring 3 のすべてについて学んでください。
- RestTemplate API について調べてください。
- JAXB リファレンス実装プロジェクトについての資料を読んでください。
- Atom/RSS のための一連の Java ユーティリティー、ROME について調べてください。ROME を使うことで、ほとんどの配信フォーマットを Java で容易に処理することができます。
- Atom についての資料を読んでください。
- developerWorks の連載記事、「Atom 出版プロトコルを知る」(2006年10月) を読んでください。Atom プロトコルの上位レベルの概要と Atom の基本的な操作と機能が解説されています。
- 高速でオープンソースの JSON プロセッサー、Jackson について学んでください。
- Spring の最新リリースを入手してください。
- Jackson をダウンロードしてください。
- IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。