• 検索結果がありません。

第 20 章 RESTfulウェブサービス RESTfulウェブサービス(Jakarta Restful Web Services)は RESTという設計原則 に基づくウェブサービスです RESTはクラウド時代のソフトエア設計法として登場 しましたが 近年 特に注目を浴びています 大きな原因はインタ

N/A
N/A
Protected

Academic year: 2022

シェア "第 20 章 RESTfulウェブサービス RESTfulウェブサービス(Jakarta Restful Web Services)は RESTという設計原則 に基づくウェブサービスです RESTはクラウド時代のソフトエア設計法として登場 しましたが 近年 特に注目を浴びています 大きな原因はインタ"

Copied!
38
0
0

読み込み中.... (全文を見る)

全文

(1)

20

RESTfulウェブサービス

 RESTfulウェブサービス(Jakarta Restful Web Services)は、RESTという設計原則 に基づくウェブサービスです。RESTはクラウド時代のソフトエア設計法として登場 しましたが、近年、特に注目を浴びています。大きな原因はインターネットの普及と コンテナ技術や大規模な運用管理技術の発達があげられます。それらにより、独立し た小さなサービスを組み合わせて、大規模システムを構成しようとするマイクロサー ビスに現実味が増してきました。RESTfulウェブサービスの仕組みを学ぶことは、マ イクロサービスを学ぶ初めの一歩です。この章を学習してクラウド時代の新しいJava を発見してください。

1 ...

(2)

20

 一般に、RESTfulウェブサービスとはRESTという構成方式を使って実装された、ネッ トワークアプリケーションを指します。では"REST"とは何でしょうか。

1.1 RESTとは

 簡単に言うと、RESTは、サーバー/クライアント間のコミュニケーション方式のひとつ です。同じようなものは他にもありますすが、JSFのように大がかりではなく、軽量で実 装も簡単ということから、クラウドネイティブな時代にマッチした方式として大きな注目 を集めています。

 RESTでは、サーバーが何かのリソース(=オブジェクトと考えてよい)を持っている時、

クライアントコンピュータからそれを操作できます。具体的には、HTTP通信のリクエス トタイプを表すGET、POST、PUT、DELETEなどを動詞(命令語)として使います。

 そのため、特定のサーバーに対するリクエストはURIとして作成し、処理に必要なパラ メータ・データなども一緒に送受信します。命令にはHTTP通信の用語しか使わないので、

プログラム言語はJava言語に限定されません。特に、クライアント側ではJavascriptがよ く使われています。

 リクエストを受信したサーバーはレスポンスを返します。レスポンスはあらゆるものが 可能です。テキストやバイナリのデータ、XML、HTML、オブジェクトやそのコレクショ ンなど、なんでも可能です。RESTの定義にはありませんが、Javascriptに対応しやすいよ うに、データをJSON形式に変換してやり取りする機会が多くなっています。

 なお、通信では、セッションを記憶することはありません。1回単位のステートレスな 通信という特徴があります。

1.2 RESTfulウェブサービスの構成方法

 RESTfulウェブサービスのサーバーへのリクエストは、次のようなURI(Uniform Resource Identifier)として作成します。

Section Section Section Section Section S Sectition Se Sectctioionn Section S i

1

S ti

RESTfulウェブサービスの概要

(3)

20

 これは、「

id

が 1 番の会員のデータを送れ」というGETリクエストです。詳細は後で説 明しますが、/sample27-01/は、作成したプロジェクト名です。そして、次の"bookshop"の 部分が作成したウェブサービスの名前です。

 "bookshop"の部分を、アプリケーション・パスといい、ウェブサービスの名前として、

設定しておく必要があります。

RESTアプリケーションパスの設定 sample27-01/bookshop/ServiceConfig.java 例題1

1 import javax.ws.rs.ApplicationPath;

2 import javax.ws.rs.core.Application;

3

4 @ApplicationPath("bookshop")

5 public class ServiceConfig extends Application {

6 }

 

Application

クラスを継承したクラスを作成して、

@ApplicationPath

アノテーショ ンで指定します。引数にサービス名("

bookshop

")を指定します。このクラスはどのパッケー ジにあってもよく、クラスの中身は空で構いません。クラス名も自由です。

 アプリケーションパスを指定したら、別のクラスを作って、具体的な処理を作成します。

次は、GETの処理を書いたサーバーサイドのプログラムです。

例題1(続き)ウェブサービスの作成 sample27-01/bookshop/MemberResource.java 1 @Path("member") // リソース名

2 public class MemberResource {

3 private Member member

4 = new Member(1L, "田中", "[email protected]","140-0014", "東京都", "品川区");

5 @GET

6 public String getMember(){

7 return member.toString();

8 }

9 }

 ウェブサービスでは、サービスをリソースごとに 作成します。そこで、最初にリソース名を宣言し ます。それを行うのがクラスに付けた

@Path

アノ テーションです。引数にリソース名を指定します(右 図を参照)。このクラスは、bookshopサービスのう ち、会員リソースに関するサービスを提供するの で、"

member

"というリソース名です。"

member

"を

次のように

改行

(4)

20

 さて、例題の

getMember()

メソッドは、

@GET

が付けられています。これにより、

GET

アクセスに応答するメソッドになります。

 

@GET

以外にも、HTTPリクエストの種類に応じて、次のようなタイプがありますが、ど れを使うかは、HTTPでの意味に準じて決めます。

アノテーション HTTP 意 味

@GET GET リソースを取得する

@POST POST リソースを新規登録する

@PUT PUT リソースを更新する

@DELETE DELETE リソースを削除する

※これ以外に@HEAD@OPTIONS@PATCHがあります

 

getMember()

メソッドは、会員オブジェクトのインスタンスを文字列表現にして返す だけです。本来ならば、パラメータとして会員

ID

を受け取り、データベースを検索した結 果を返すところです。ここでは構成方法を示すだけなので簡単にしています。

 

Member

クラスはプロジェクトの

entity

パッケージに入っています。次のようなクラス で

toString

メソッドが定義されています。

例題1(続き)会員エンティティ sample27-01/entity/Member.java 1 @Entity

2 public class Member implements Serializable {

3 private static final long serialVersionUID = 1L;

4 @Id

5 private Long id;

6 private String name; // 氏名 7 private String mail; // メール 8 private String zip; // 郵便番号 9 private String pref; // 10 private String city; // 市区町村 11 // 以下省略

12 }

1.3 クライアントプログラムで実行する

 ウェブサービスができたので、次はクライアント側から実行してみましょう。クライア も

(5)

20

 本章では、これらのクライアントを順番に使ってみますが、最初は、ブラウザを使ってサー バーにGETアクセスしてみましょう。。NetBeansでsample27-01プロジェクトを起動した後、

次のURIを開きます。

http://localhost:8080/sample27-01/bookshop/member

※payara serverはHTTP通信にポート8080を使うので、ポート指定が必要です

 実行すると、次のように、

member.toString()

が出力した文字列が表示されます。

トルツメ

(6)

20

2.1 サブURI

 リソースについての処理を、細かく分けたい場合は、サブURIを指定できます。サブ URIはメソッドに

@Path

アノテーションを付けて指定します。

サブURIの指定 sample27-02/bookshop/MemberResource.java 例題2

1 @Path("member")

2 public class MemberResource {

3 private Member member

4 = new Member(1L, "田中", "[email protected]","140-0014", "東京都", "品川区");

5 @GET

6 @Path("/address")

7 public String getMemberAddress() {

8 return member.getZip()+member.getPref()+member.getCity();

9 }

10 @GET

11 public String getMember() {

12 return member.toString();

13 }

14 }

 例題は、

getMemberAddress()

メソッドに

@Path("/address")

を指定しています。

このメソッドをGETアクセスするURIは、リソース名に

/address

を連結したものになり ます。

http://localhost:8080/sample27-02/bookshop/member/address

 ブラウザからGETアクセスすると次のような応答が返されます。

Section Section Section Section Section S Sectition Se Sectctioionn Section S i

2

S ti

サブURIの指定とPathパラメータ

住所情報を処理するサブURIの指定

(7)

20

2.2 Pathパラメータ

 URIの中に、何かの値をパラメータとして埋め込むことができます。例えば、会員番号 をPathパラメータとして埋め込むには、URIを次のように指定します。

http://localhost:8080/sample27-02/bookshop/member/1

 末尾の "1" がPathパラメータで、会員番号に1を指定しています。

 パラメータを受け取るには、サーバーサイドは、次のように書く必要があります。

@Path("member")

public class MemberResource @GET

@Path("{id}")

public String getMember(@PathParam("id") Long id) { ・・・

 

getMember()

メソッドに

@Path("{id}")

を付けたので、URIは次のようになりますが、

{"id"}

は、idという名前のパラメータを意味します。

http://localhost:8080/smaple27-01/bookshop/member/{"id"}

 したがって、

{"id"}

の部分には、1とか2のようなパラメータの値を指定できます。

 指定されたパラメータは、

getMember()

メソッドの引数に代入されます。そこで、値 を受け取る引数には、

@PahtParam("id")

というアノテーションを付けておきます。

public String getMember(@PathPram("id") Long id){

 このようにすると引数

id

に、URIで指定された値を受け取ることができます。

 次は、このPathパラメータ指定を適用した例題です。

トルツメ

トルツメ

(8)

20

【注意】これ以降の例題ではデータベースを使います。

例題を実行する前に、sample27-dbプロジェクトを実行して、データ ベースを作成しておいてください。

実行すると図のような表示がでるので、[実行]ボタンを押すだけです。

これにより、5件のメンバーを登録したmemberテーブルができます。

Bb.javaを見てデータ内容を確認してください。

Pathパラメータの受取り sample27-02B/bookshop/MemberResource.java 例題3

1 @Stateless

2 @Path("member")

3 public class MemberResource {

4 @PersistenceContext

5 EntityManager em;

6 @GET

7 @Path("{id}")

8 public String getMember(@PathParam("id") Long id) {

9 Member member = em.find(Member.class, id);

10 return member.toString();

11 }

12 }

 例題は、データベースを使うので、

@Stateless

を付けてステートレス・セッションビー ンにしています。会員番号を引数

id

に受け取っているので、それを使ってデータベース を検索します。

 最後に、取得した

Member

エンティティの文字列表現を返すことは、これまでの例題と 同じです。sample27-02Bを実行し、ブラウザでURIにアクセスすると次のようになります。

idとして2を指定

同一サイズで、

8430.tif に差替え↑8430なし

ステートレスセッションビーンにする

データベースを検索

送ります

(9)

20

 もう1つのパラメータの指定方法として、Queryパラメータがあります。普通のウェブで もGETアクセスのパラメータ指定に使う形式です。例えば、次のようなURIです。

http://localhost:8080/sample27-03/bookshop/member?id="2"

 これは

id

という名前で値が2であるパラメータを指定しています。Queryパラメータで は、これ以外にも、複数のパラメータを指定したり、デフォルト値を決めておいたりでき るので便利です。

3.1 Queryパラメータ

 次は、例題2を、Queryパラメータに変更したものです。idをパラメータとして受け取り、

Member

エンティティを検索して返します。

Queryパラメータの受け取り sample27-03/bookshop/MemberResource.java 例題4

1 @Stateless

2 @Path("member")

3 public class MemberResource {

4 @PersistenceContext

5 EntityManager em;

6 @GET

7 public String getMember(@QueryParam("id") Long id) {

8 Member member = em.find(Member.class, id);

9 return member.toString();

10 }

11 }

 Queryパラメータでは、メソッドの引数に

@QueryParam("id")

を指定して、パラメータ を受け取ります。単にパラメータ名を指定するだけので、Pathパラメータよりもシンプルです。

 検索処理は例題2と同じなので解説は省略します。クラスに

@Stateless

を指定してい ることに注意してください。例題を実行してブラウザでアクセスした結果を示します。

Section Section Section Section Section S Sectition Se Sectctioionn Section

S i

3

S ti

Queryパラメータ

3

3

(10)

20

http://localhost:8080/sample27-03/bookshop/member?id="5"

3.2 Querayパラメータのデフォルト値

 Queryパラメータでは、何も値を指定しなかった場合のデフォルト値を決めておくこと ができます。

デフォルト値の指定 sample27-03B/bookshop/MemberResource.java 例題5

1 @Stateless

2 @Path("member")

3 public class MemberResource {

4 @PersistenceContext

5 EntityManager em;

6 @GET

7 public String getMember(@DefaultValue("1") @QueryParam("id") Long id) {

8 String jpql = "select e from Member e where e.id=?1";

9 Member member = em.find(Member.class, id);

10 return member.toString();

11 }

12 }

 

@DefaultValue()

アノテーションをメソッドの引数に付けて、()内にデフォルト値を 指定します。

Long

などの数値でも、文字列の形式で指定することに注意してください。

 例題を実行し、パラメータを指定しないURIでアクセスすると次のように、デフォルト 値(=1)が有効になっていることがわかります。

http://localhost:8080/sample27-03/bookshop/member

(11)

20

3.3 複数のQueryパラメータ

 Queryパラメータはいくつでも指定できます。次は2つのパラメータを指定する例です。

複数のQueryパラメータ sample27-03C/bookshop/MemberResource.java 例題6

1 @Stateless

2 @Path("member")

3 public class MemberResource {

4 @PersistenceContext

5 EntityManager em;

6 @GET

7 @Path("/address")

8 public List<Member> memberByAddress(@QueryParam("pref") String pref,

9 @QueryParam("city") String city) {

10 String jpql = "select e from Member e where e.pref=?1 and e.city=?2";

11 List<Member> members = em.createQuery(jpql, Member.class)

12 .setParameter(1,pref)

13 .setParameter(2,city)

14 .getResultList();

15 return members;

16 }

17 }

 パラメータがたくさんある時は、その数だけ

@QueryParam

アノテーションを書きます。

例題はJPQLを使って、

pref

(都府県)と

city

(市区)という2つのパラメータを受け取っ て、その条件で会員を検索します。なお、会員の住所情報を扱うので、サブURIとして

"

/address

"を指定しています。

 例えば、東京都で品川区に住んでいる会員を求めるURIは次のようになります。

http://localhost:8080/sample27-03C/bookshop/member/address?pref=東京都&city=品川区

 文字列を引用符で囲わないことに注意してください。また、2つ目以降のパラメータは&

を使って連結します。

 例題を実行し、ブラウザで上記のURIをアクセスした場合の結果を示します。

Listをそのまま返す

(12)

20

 検索結果は

Member

List

で、例題は

List

をそのまま返したため、このような表示に なっています。これはJSON形式ですが、ブラウザの表示では見にくくなります。そこで、

もう少し便利なRESTクライアントを使うことにしましょう。

3.4 通信テストプログアラムの利用

 通信テストプログアラムをインストールすると、GETの結果を見やすくできるだけでな く、POSTやPUT、DELETEなどのURIも実行できます。また、実行時に、HTTPヘッダー を編集してサーバーに適切な指示を出せるようになります。

インストール

 ここではPostmanという通信テストプログアラムをインス トールします。次のURLからダウンロード・インストールし てください。

 

https://www.postman.com/downloads/

ウェブバージョンではなく、「Download the app」をクリック します。ダウンロードファイルをダブルクリックしてインス トールし、IDやパスワードを登録しますが、画面の指示に従っ て、インストールを終了してください。

 MacもWindowsと基本的に同じ手順です。

起動

 インストールして起動すると"Launchpad"タブが選択された状態の画面が開きます。

 この画面で、「create a request」を選んでください。

これをクリックする

トルツメ

トルツメ

トルツメ

(13)

20

URIの作成と送信

 URIの入力画面になるので、例題6で入力したURIを次の手順で入力して送信します。

①入力欄にQueryパラメータ以外の部分を入力する

  http://localhost:8080/sample27-03C/bookshop/member/address

②Query Params欄にパラメータを入力する ⇒ URI欄には自動的に追加される

 

③[send]ボタンを押す

④実行結果がResponse欄に表示される

 

 Postmanを使って実行すると、取得した2件のJSON形式のデータが、わかりやすく表示 されます。これは要素が2つのJSONデータの配列表現です。

(14)

20

4.1 MIMEタイプ

 前節の例では、レスポンスとして

List

をそのまま返したので、データがJSON形式になっ ていました。一般に、送受信するデータ形式を指定しない場合は、MIMEタイプとして

"

*/*

"が設定されているものとみなされ、自動的に適切なタイプが使われます。

 データ形式は、IANA(Internet Assigned Numbers Authority)によりMIMEタイプとし て定義されています。例えば、次のようなものがあります。Jakarta Restful Web Services では

jakarta.ws.rs.core.MediaType

クラスでクラス変数として定義しています。

MIMEタイプ 意味 MediaTypeクラスのクラス変数

text/plain テキスト形式 TEXT_PLAIN

text/xml, Application/xml XML形式 TEXT_XML, APPLICATION_XML

Application/json JSON形式 APPLICATION_JSON

text/html HTML形式 TEXT_HTML

 プログラムでは、

@Produces

アノテーションを使ってクライアントに返すデータ形式を 指定し、

@Consumes

アノテーションを使ってクライアントから受け取るデータ形式を指定 します。次に、その具体的な方法を解説します。

4.2 @ProducesとMIMEタイプの選択メカニズム

 2つのアノテーションは、原則としてクラスに付けますが、必要な場合はメソッドにも指 定して、クラスでの定義を上書きできます。

 また、サーバーサイドでは、MIMEタイプを複数指定しておくことができます。実際に どのMIMEタイプを適用するかは、クライアント側の指定によります。クライアントは、

送信するデータのMIMEタイプをcontentsヘッダで、また、受け取るデータのMIMEタイ プをAcceptヘッダで指定できます。なお、クライアント側で指定しない場合は、サーバー 側で自動的に適切なMIMEタイプが選択されます。

Section Section Section Section Section S Sectition Se Sectctioionn Section S i

4

S ti

HTTPとメディアタイプ

RESTful

(15)

20

MIMEタイプの選択 sample27-04/bookshop/MemberResource.java 例題7

1 @Stateless

2 @Path("/member")

3 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

4 public class MemberResource {

5 @PersistenceContext

6 EntityManager em;

7 @GET

8 public Member getMember(@QueryParam("id") Long id) {

9 Member member = em.find(Member.class, id);

10 return member;

11 }

12 }

 例題の、

getMember

メソッドは、会員番号で会員を検索して返すサービスで、会員番 号をQueryパラメータで受け取ります。

 3行目の

@Produces

は、出力するMIMEタイプを指定します。複数のMIMEタイプを指 定する時は、

{ }

の中に、コンマで区切って並べます。

@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

 この指 定により、MemberエンティティをXML形 式とJSON形 式で 返 すことが で きます。ただ、XML形 式 で 返 せるようにするためには、

Member

エンティティに

@ XmlRootElement

アノテーションを付けておく必要があります。

例題7(続き)XMLに変換できるエンティティ sample27-04/entity/MemberResource.java 1 @XmlRootElement

2 @Entity

3 public class Member implements Serializable {

4 private static final long serialVersionUID = 1L;

5 @Id

6 private Long id;

7 private String name; // 氏名 8 private String mail; // メール // 以下省略

※XMLをオブジェクトにマップするためのJAXB(Jakarta XML Binding)で使われるアノテーションです。これによ

1行 アケル

次の行に送る

(16)

20

 では、sample27-04を実行し、Postmanから呼び出してみましょう。

http://localhost:8080/sample27-04/bookshop/member?id=1

XML 形式

 XML形式で返ってきたのは、受け取りのMIMEタイプを指定していなかったので、サー バー側で適切なタイプを選択したからです。送信時のヘッダーがどうなっていたか調べて みましょう。それには、Postmanの左下端にあるConsoleボタン( )を押します。す ると、次のようにヘッダ情報が表示されます。

特に指定していない

サーバーはxmlで返してきた

(17)

20

 では、Acceptヘッダに"

application/json

"を指定して、JSON形式で受け取ってみま しょう。Postmanではヘッダ情報を設定するには、Pre-requestScriptをクリックします。

クリックする

 入力欄が開くので、次のように入力します。

pm.request.headers.add({key: 'Accept', value: 'application/json'});

 Postmanはコマンドでヘッダ情報を設定します。

'Accept

'と'

application/json

'の部 分を変更すると、他のヘッダも設定できます。

 設定したら[Send]ボタンを押して実行します。応答ではJSON形式のデータが返されます。

まだヘッダ情報もJSONとなっていることがわかります。

(18)

20

4.3 @Consumes

@Consumes

は、サーバーサイドがPOSTやPUTなどで受け入れるデータのMIMEタイプ を指定します。そこで、

@POST

に対応する

createMember()

メソッドを作成して、動作 を確認しましょう。

リソースの追加 sample27-04B/bookshop/MemberResource.java 例題8

1 @Stateless

2 @Path("/member")

3 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

4 @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

5 public class MemberResource {

6 @PersistenceContext

7 EntityManager em;

8 @GET

9 public Member getMember(@QueryParam("id") Long id) {

10 Member member = em.find(Member.class, id);

11 return member;

12 }

13 @POST

14 public String createMember(Member member) {

15 if(member==null) throw new BadRequestException();

16 em.persist(member);

17 return "created";

18 }

19 }

 4行目に、

@Consumes

アノテーションを付けてXMLとJSON形式のデータを受け入れる ように定義しています。青枠内が

Member

インスタンスを受け取ってデータベースに登録 する

createMember()

メソッドで、

@POST

が付けてあります。

@POST

は、新しくリソース(=

オブジェクト)を作成・追加するメソッドに付けるアノテーションです。

 

createMember()

メソッドの引数が

Member member

となっていることからわかるよう に、ウェブサービスクライアントが、

Member

インスタンスをPOSTで送信してくること を予定しています。データ形式はクライアントのContent-Typeヘッダを見てXMLかJSON に自動的に設定されます。

createMember()

メソッドは、受け入れたインスタンスをその ままデータベースに登録します。

追加した メソッド

(19)

20

す。この例外はJakarta Restful Web Servicesで定義されている実行時例外で、HTTPの 応答に変換されます。

 jakarta.ws.rsパッケージには、WebApplicationException以下、次のような実行時例外 が定義されているので、必要に応じて使うことができます。

 ではsample27-04Bを実行し、PostmanからPOSTで

Member

インスタンスを送ってみま しょう。

POST送信の手順

①○+ボタンを押して新しいタブを開く  

②送信種別をPOSTに切り替え、URIを入力する。

 

http://localhost:8080/sample27-04B/bookshop/member

③[Body]⇒[raw]と選択し、[Text]をクリックして選択肢からJSONを選ぶ

 

RESTful

(20)

20

④前回実行したGETのタブをクリックして表示する(JSONデータをコピーするため)

 

⑤コピーボタンを押してJSONデータをコピーする

 

⑥POSTのタブをクリックして戻る

⑦JSONデータの入力欄で右ボタンを押して、JSONデータを貼り付ける

 

JSONになっているか確認

⑧JSONデータを次のように修正する(画面では表示順がABC順になっているが構わない)

 

id 6

name 中村

mail [email protected] zip 221-0014

pref 神奈川県

city 横浜市

(21)

20

⑨[Send]ボタンを押す ⇒ createdと200 OKというリターンコードが表示される

 

 以上で、サーバーへの送信が終わり、データベースに書き込まれたことがわかります。

念のため、NetBeansでMemberテーブルの内容を参照すると、次のようになっています。

 

 同様にしてXMLデータを送信してもエンティティを作成できます。例えば、次のデータ を使って、各自でやってみてください。送信データ種別をJSONからXMLに変え、データ 欄を消して代わりに次のデータを書き込みます。後は[Send]ボタンを押すだけです。

<member>

<city>横浜市</city>

<id>7</id>

<mail>[email protected]</mail>

<name>佐々木</name>

<pref>神奈川県</pref>

<zip>221-0014</zip>

</member>

(22)

20

5.1戻り値に使うResponseクラス

 すでに見たように、RESTサービスのメソッドの戻り値には、どんな値でも使うことが できますが、実際にはResponseクラスのインスタンスを返すのが普通です。Responseク ラスは、レスポンスヘッダーや返す値(リソース)をひとつにまとめて保持するクラスです。

 主に次の表に示すメソッドを使って、レスポンスを作成します。

Responseクラスのクラスメソッド

主なメソッド ステータス(番号) 意味・説明

ok() OK(200) OKステータスのみのレスポンス

ok(entity) OK(200) エンティティをセットして返す

ただし、entityObject型(以下も同様)

ok(entity, mediaType) OK(200) エンティティとメディアタイプをセットし

て返す

ok(entity, type) OK(200) エンティティとメディアタイプ文字列を

セットして返す

ok(entity, variant) OK(200) エンティティとVariantオブジェクトを

セットして返す。Variant型はローケル、

エンコーディング、言語などの情報を含む オブジェクト

created(uri) CREATED(201) 作成したリソースへのURIをセットして返

accepted(entity) ACCEPTED(202) リクエストを承認したというステータスと

エンティティをセットして返す

noContent() NO_CONTENT(204) 要求を実行したがエンティティは返さない

seeOther(uri) SEE_OTHER(303) POSTした後にリダイレクトするパターン

で、リダイレクトURIをセットして返す

notModified() NOT_MIDIFIED(304) 変更なしというステータスをセットして返

notModified(tag) NOT_MIDIFIED(304) 同上。ただし、エンティティを指すタグを 含めて返す

temporaryRedirect(uri) TEMPORARY_REDIRECT (307)

要求されたリソースが一時的に別のURI あるので、リダイレクトURLをセットして返

Section Section Section Section Section S Sectition Se Sectctioionn Section S i

5

S ti

戻り値の作成

(23)

20

主なメソッド ステータス(番号) 意味・説明

serverError() サーバーエラー

(500番台の値) 何らかのサーバエラーを示す

status(status) セットしたステータス enum型のStatus、または、int型のステー タス番号をセットして返す

stats(status, reason) セットしたステータス int型のステータス番号と理由を示す文字 列をセットして返す

 これらは、

Response

クラスのクラスメソッドで、ステータス情報と共に、オブジェク トやURIなどを返すことができます。

 これらのメソッドの戻り値は(メソッドを連結できるように)どれも

ResponseBuilder

型になっています。メソッドチェーンを終了するには、

Response

型の値を返す

build()

メソッドを、末尾に連結する必要があります。

 では、

Respose

を作成する具体的な方法をいくつか示します。

// OKステータスを返す

return Response.ok().build();

// Memberクラスのmemberエンティティを返す return Response.ok(member).build();

// MemberクラスのmemberエンティティをJSON形式で返す

return Response.ok(member, MediaType.APPLICATION.JSON).build();

// 作成されたことを示すリターンコード(201)を返す

return Response.status(Status.CREATED).build();

// 作成・登録したMemberエンティティにアクセスできるURIを付けて返す return Response.created(memberUri).build();

// 削除処理の結果を返す。指定されたエンティティを削除したので、「エンティティは存在しなくなった」の意味

return Response.noContent().build();

5.2 CRUDのウェブサービス

 戻り値に

Response

を使うCRUD形式のプログラムを作成します。前節ではGET(検 索)、POST(新規登録)の処理を作ったので、ここではUPDATE(更新)、DELETE(削除)、

GET(全件検索、同じGETでもURIが違う)を追加して作成します。また、すべての戻り値 を

Response

オブジェクトで書き換えます。

青太字にする

(24)

20

 なお、

@get

処理に変更があります。パラメータ受け渡しのバリエーションを示すため、

この例題ではクエリパラメータの代わりにパスパラメータを使っています。例題でアンダー ラインで示している9、10行目に注意してください。

 やや長いソースコードですが、基本的に、データベースの章で解説したものと同じ内容 です。各メソッドの働きを確認しながら目を通してください。

CRUDのウェブサービス sample27-05/bookshop/MemberResource.java 例題9

1 @Stateless

2 @Path("/member")

3 @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

4 @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})

5 public class MemberResource {

6 @PersistenceContext

7 EntityManager em;

8 @GET // 検索 9 @Path("{id}")

10 public Response getMember(@PathParam("id") Long id) {

11 Member member = em.find(Member.class, id);

12 if(member==null) return Response.status(Status.NOT_FOUND).build();

13 return Response.ok(member).build(); // 200 14 }

15 @POST // 登録

16 public Response createMember(Member member) {

17 if(member==null) return Response.status(Status.BAD_REQUEST).build();

18 em.persist(member);

19 return Response.status(Status.CREATED).build(); // 201 20 }

21 @PUT // 更新

22 public Response updateMember(Member member) {

23 if(member==null) return Response.status(Status.BAD_REQUEST).build();

24 em.merge(member);

25 return Response.ok().build(); // 200 26 }

27 @DELETE // 削除

28 public Response deleteMember(@QueryParam("id") Long id) {

29 Member member = em.find(Member.class, id);

30 if(member==null) return Response.status(Status.NOT_FOUND).build();

31 em.remove(member);

32 return Response.noContent().build(); // 204

(25)

20

36 String jpql = "select e from Member e";

37 List<Member> list = em.createQuery(jpql, Member.class).getResultList();

38 return Response.ok(list).build();

39 }

40 }

 

@PUT

は更新処理、

@DELETE

は削除処理です。また、クエリパラメータがない

GET

は全 件検索の呼び出しになります。どれもエンティティマネージャーでの処理ですから、解説 は省略します。

 処理が失敗する場合は、例外を投げるのではなく適切なステータスコードを返すように します(下線部分)。

 主なステータスには、次のようなものがあります。

ステータス 意味

OK 200 OK

CREATED 201 作成

ACCEPTED 202 承認済み

NO_CONTENT 204コ ンテンツなし

FOUND 302 見つかりました

BAD_REQUEST 400 BadRequest

FORBIDDEN 403 禁止されています

NOT_FOUND 404 見つかりません

REQUEST_TIMEOUT 408 RequestTimeout

CONFLICT 409 競合

 また、例題のソースコードに示しているように、正常終了の場合に返すステータスは、

次のように決まっています。

動作 正常終了で返すステータスコード POST 201CREATED

GET 200OK PUT 200OK

DELETE 204No Content

 では、PostmanからPOST、GET、PUT、DELETEを実行してみましょう。

 以下の図をみながら、同じように実行してみてください。

(26)

20

POST    

id=7のMemberを新規登録する

送信データ(JSON 形式)

GET    

id=6のレコードを検索する

検索結果の受信データ 5

9340.tif に差替え サイズ、

枠線は 同じ 180 を挿入 図との行間は 0にする

182 を挿入 図との行間は 0にする

(27)

20

PUT    

id=7のレコードのcity項目を川崎市に変更する

送信データ(JSON 形式)

DELETE    

id=7のレコードを削除する 184 を挿入

図との行間は 0にする

186 を挿入 図との行間は 0にする

(28)

20

GET(全件検索)

188 を挿入 図との行間は 0にする

(29)

20

 Jakarta Restful Web Servicesにはクライアントを作成するクライアントAPIがあります。

RESTサービスのクライアントをJava言語で作成できるので、いろいろな場面で活用でき ます。クライアントAPIは、StatelessセッションビーンやCDIビーンからも利用できます。

ここでは、Javaクライアントからサービスにアクセスする方法を具体的に解説します。

6.1 CRUD処理の概要

 前節のsample27-05で作成したRESTfulウェブサービスを、クライアントAPIを使って、

Javaプログラムからアクセスします。

 内容は、15章の例題2(sample22-01プロジェクト)と同様のCRUD処理です。ただ、15章 ではデータベースに直接アクセスしていましたが、ここでは、ウェブサービスにアクセス します。

 ウェブ画面の構成は次のようです。sample27-05を実行した上で、この例題を実行して 動作を確認してください。

Section Section Section Section Section S Sectition Se Sectctioionn Section

S i

6

S ti

クライアントAPI

RESTful

(30)

20

6.3 クライアントAPIの使い方

 解説の前に、例題のソースコードを示しておきます。青字の部分に注意してください。

Client APIの使い方 sample27-06/beans/Bb.java 例題10

1 @Named

2 @ViewScoped

3 public class Bb implements Serializable {

4 @NotNull

5 private Long id; // ID 6 private String name; // 氏名 7 private String mail; // メール 8 private String zip; // 郵便番号 9 private String pref; // 10 private String city; // 市区町村 11

12 Client client; // Client API 13 WebTarget target; // Client API 14 @PostConstruct

15 public void initClient() { // 最初にアクセスするリソースを設定しておく 16 client = ClientBuilder.newClient();

17 target

18 = client.target("http://localhost:8080/sample27-05/bookshop/member");

19 }

20 public void find() { // 検索 21 Response res = target

22 .path(String.valueOf(id))

23 .request()

24 .get();

25 if (res.getStatus() == Status.OK.getStatusCode()) {

26 Member member = res.readEntity(Member.class);

27 this.id = member.getId();

28 this.name = member.getName();

29 this.mail = member.getMail();

30 this.zip = member.getZip();

31 this.pref = member.getPref();

32 this.city = member.getCity();

33 } else {

34 clear();

35 this.name = "該当なし";

36 } 190

を挿入

削除

(31)

20

38 public void create() { // 新規作成

39 Member member = new Member(id, name, mail, zip, pref, city);

40 Response res

41 = target

42 .request()

43 .post(Entity.entity(member, MediaType.APPLICATION_JSON));

44 if (res.getStatus() == Status.CREATED.getStatusCode()) clear();

45 }

46 public void update() { // 更新

47 Member member = new Member(id, name, mail, zip, pref, city);

48 Response res

49 = target

50 .request()

51 .put(Entity.entity(member, MediaType.APPLICATION_JSON));

52 clear();

53 }

54 public void delete(Member member) { // 削除 55 Response res = target

56 .queryParam("id", member.getId())

57 .request()

58 .delete();

59 }

60 public List<Member> getAll() { // 全データをListに入れて返す 61 Response res = target

62 .request()

63 .get();

64 return res.readEntity(new GenericType<List<Member>>(){});

65 }

66 public void edit(Member member) { // 編集のため変数にコピーする 67 this.id = member.getId();

68 this.name = member.getName();

69 this.mail = member.getMail();

70 this.zip = member.getZip();

71 this.pref = member.getPref();

72 this.city = member.getCity();

73 }

74 public void clear() { // 変数をクリア 75 id = null;

76 name = mail = zip = pref = city = null;

77 }

78 @PreDestroy

79 public void close(){

80 client.close();

81 }

82 // セッター・ゲッターの掲載を省略 83 }

(32)

20

ウェブサービスをポイントするWebTargetインスタンスの作成

 クライアントAPIを利用するには、最初に

Client

オブジェクトを作ります。

Client

オ ブジェクトは重量級のオブジェクトなので、プログラムの最初で1度だけ作成します。そこ で、普通は、

@PostConstract

を付けたメソッドの中で作成します。

Client client;

WebTarget target;

@PostConstruct

public void initClient(){

client = ClientBuilder.newClient();

target

= client.target("http://localhost:8080/sample27-05/bookshop/member");

}

 また、

Target

はリソースのURIをセットするオブジェクトで、Clientのインスタンスか ら作成し、それを使ってサービスにアクセスします。アクセス先に応じて必要な時に作成 して構いませんが、例題では同じURIしか使わないので、

Client

オブジェクトと一緒に 作成しています。

WebTargetの使い方の基本

 

WebTarget

インスタンスは、図のようにメソッドチェーンで使います。図は、代表的な 使い方を示しています。図で、[ ]の部分は必要なければ省略できます。

 

path()

queryParam()

はパラメータをセットします。また、

accept()

はMIMEタ イプを指定し、

header()

は任意のHTTPヘッダを付加します。

 例えば、次のようにすると

get

リクエストを実行して、

id

が3のレコードを

Response

オ ブジェクトで受け取ることができます。

8440.tif に差替え↑8440なし RESTfulウェブサービスのURIを指定する

この位置に、この大きさで この文字列を挿入

送付します 8840.tif に差替え

(33)

20

Response res = target .path("3") .request()

.get(); // getリクエストを実行して結果を受け取る

 このほかにも使えるメソッドがありますが、Jakarta EE のAPIを参照してください。

jakarta.ws.rs.client

パッケージに、

Client

クラス、

WebTarget

クラスがあります。

また、

jakarta.ws.rs.core

パッケージには、

Response

クラスの詳細が掲載されてい ます。

 では、例題のコードを見ていきましょう。

findメソッド(20行目)

 

id

をパスパラメータで指定して、GETアクセスでレコードを取得します。

path()

の引 数は文字列なので、

String.valueOf(id)

のように文字列にして指定します。

Response res = target

.path(String.valueOf(id)) .request()

.get();

if (res.getStatus() == Status.OK.getStatusCode()) { Member member = res.readEntity(Member.class); ・・・

 

Response

オブジェクトから検索した

Member

オブジェクトを取り出すには、

Response

クラスの

readEntity()

メソッドを使います。オブジェクトの型情報(

Member.class

) を引数に指定する必要があります。

 なお、

readEntity()

メソッドだけでなく、

Response

オブジェクトのメソッドを使って、

様々な情報を得ることができます。次の表によく使うメソッドを示します。

メソッド 機能

String getHeaderString(String name) nameで指定したヘッダー文字列を得る

Locale getLanguage() ローケルを得る

URI getLocation() エンティティにアクセスするURIを得る

MediaType getMediaType() メディアタイプを得る

int getStatus() ステータスを得る

T readEntity(Class<T> entityType) 型を指定してエンティティを得る

※これは主なメソッドの抜粋です。詳細はjakarta EE apiドキュメントを参照してください https://jakarta.ee/specifications/platform/9/apidocs/

本文中の参照元

※不明 // Pahtパラメータとして3をセットする

続けて

表のメソッドに 表

(34)

20

create()メソッド(38行目)

 

Member

クラスのインスタンスを作成して、POSTアクセスによりデータベースに登録し ます。

Member member = new Member(id, name, mail, zip, pref, city);

Response res = target .request()

.post(Entity.entity(member, MediaType.APPLICATION_JSON));

 送信する

Member

オブジェクトは、

Entity

クラスでラップしますので、

entity

メソッ ドの引数に、送信するオブジェクトとそのメディアタイプを指定します。

update()メソッド(46行目)

 PUTアクセスによりデータベースのレコードを更新します。

Member member = new Member(id, name, mail, zip, pref, city);

Response res = target .request()

.put(Entity.entity(member, MediaType.APPLICATION_JSON));

 クライアントAPIの使い方は、

post

と同じで、メソッドを

put

に変えるだけです。

delete()メソッド(54行目)

 

id

をクエリパラメータで指定して、DELETEアクセスでレコードを削除します。クエリ パラメータを指定する

queryParam()

では、引数に主キーのフィールド名と値を指定しま す。

Response res = target

.queryParam("id", member.getId()) .request()

.delete();

青下線

青下線

(35)

20

Response

オブジェクトから取り出すには、

readEntity()

メソッドを使いますが、その 引数の書き方に注意してください。

Response res = target .request() .get();

return res.readEntity(new GenericType<List<Member>>(){});

 

List

のような複数のオブジェクトの集まりを受け取るには、何かのクラスにラップして 受け取る必要があります。従来は、ラップするクラスを別に作成して使っていました。例 えばこのケースでは、

ArraysList

を継承したクラスを作成する必要があります。

 

GenericType

クラスはそれを簡単にするために設けられた総称型のクラスで、任意の 型を表すオブジェクトです。

<List<Member>>

のように具体的な型を指定してサブクラ スを生成することで、いろいろな型を表すことができます。

new GenericType<List<Member>>(){} // 匿名サブクラスのインスタンスを生成

 なお、右端にある

{ }

に疑問を持つかもしれませが、これは指定したクラス(ここでは

GenericType

)のサブクラスのインスタンスを生成する書き方です。サブクラスの名前が 指定しないので匿名サブクラスといいます。これにより、

GenericType

クラスの無名の サブクラスを定義すると共に、そのインスタンスが作成されます。

 Jakart EE のAPIドキュメント(

jakarta.ws.rs.core.GenericType<T>

を参照)に もこの使い方が示されています。

(注)匿名クラスについては「わかりやすいJavaオブジェクト指向徹底解説」をご覧ください。

 定型的な書き方ですから、いつもこのように書いてください。これにより、

Member

の リストを受け取ることができます。

Clientオブジェクトをcloseする(78行目)

 

client

オブジェクトは、コンテンツを読み取った後で自動的に

close

されますが、通 信エラーなどが発生し、そのまま残ってしまうことがあります。そこで、

@PreDestroy

を付けたメソッドの中でクローズしておきます。これによりバッキングビーンが消去され る直前に

client

オブジェクトも自動的にクローズされます。

lv4(0.0 の数字 のつく形式)の 指示があったが 他に合わせた

1行アケル

?です

(36)

20

要点

※理解した項目にはチェックを入れましょう

RESTfulウェブサービスの構成

□ Applicationクラスを継承したクラスに@ApplicationPathを書いてアプリケーションパス を設定する

 ・public なクラスで、パッケージとクラス名は自由、メソッドなどは書かない

□ HTTPの動詞(GET、POST、PUT、DELETEなど)を使ってサービスを構成する

□ サービスはリソースごとに、1つのクラスとして作成する

 ・クラスには@Pathを付けてリソースを設定する -- @Path("member")など

□ サービスを提供するメソッドには、@GET、@POST、@PUT、@DELETEなどを付ける

□ クライアントは、URIを指定してリソースにアクセスする

 ・http://localhost:8080/sample27-01/bookshop/member

□ PathパラメータやQueryパラメータを使ってURIと共に送信できる  ・http://localhost:8080/sample27-02/bookshop/member/1  ・http://localhost:8080/sample27-02/bookshop/member?id="1"

□ Queryパラメータには@DefaultValue()によって、指定しなかった場合の既定値を設定できる

HTTPとメディアタイプ

□ @Producesは送信するデータのMEDIAタイプを指定する。複数のタイプを指定できる

□ @Consumesは受信するデータのMEDIAタイプを指定する。複数のタイプを指定できる

□ @Producesと@Consumesはクラスに指定するが、メソッドに付けるとクラスの指定を上書きで きる

□ クライアントは、Acceptヘッダを指定して受信するタイプを指定できる

□ クライアントは、Content-Typeヘッダで送信するタイプを指定できる

□ XML形式で送受信するエンティティには@XmlRootElementアノテーションを付ける

Responseクラスは戻り値として使う

Section Section Section Section Section S Sectition Se Sectctioionn Section S i

7

S ti

まとめ

(37)

20

□ サービスでエラーが発生した時は、例外を投げる方法もあるが、Responseにステータスをセッ トするとクライアントで対策が取りやすくなる

クライアントAPI

□Clientオブジェクトは@PostConstructを付けたメソッドの中で作成する

□Clientオブジェクトは、@PreDestroyを付けたメソッドの中でclose()する

□Clientインスタンスを使ってURIをセットしたTargetオブジェクトを作成する

□リソースにアクセスしてResponseを得るには次のようなメソッドチェーンを使う

<∇≡挿入>☆8440.tif 84%

□Responseからオブジェクトを得るにはreadEntityメソッドを使う  Member member = res.readEntity(Member.class);

□Responseからオブジェクトのリストを得るにはGenericTypeクラスでListの型を指定する。

 List<Member> list = res.reanEntity(new GenericType<List<Member>>(){});

8440ない

今回、送付します

(38)

20

参照

関連したドキュメント

共通点が多い 2 。そのようなことを考えあわせ ると、リードの因果論は結局、・ヒュームの因果

キャンパスの軸線とな るよう設計した。時計台 は永きにわたり図書館 として使 用され、学 生 の勉学の場となってい たが、9 7 年の新 大

基準の電力は,原則として次のいずれかを基準として決定するも

○池本委員 事業計画について教えていただきたいのですが、12 ページの表 4-3 を見ます と、破砕処理施設は既存施設が 1 時間当たり 60t に対して、新施設は

これからはしっかりかもうと 思います。かむことは、そこ まで大事じゃないと思って いたけど、毒消し効果があ

モノづくり,特に機械を設計して製作するためには時

自然言語というのは、生得 な文法 があるということです。 生まれつき に、人 に わっている 力を って乳幼児が獲得できる言語だという え です。 語の それ自 も、 から

大村 その場合に、なぜ成り立たなくなったのか ということ、つまりあの図式でいうと基本的には S1 という 場