CData API Serverを使って既存のWebサービスにRESTfulなAPIを追加してみた

#はじめに
最近のWebサービスは外部のサービスと連携することが当たり前になってきています。そのためにはAPI機能が必要になるわけですが、APIを持たないWebサービスも数多くあるかと思います。追加しようにもコストも時間も必要ですし、使いづらいAPIは使ってもらえません。そこで本記事では、CData API Serverを使って、Webサービスに加える修正は最小限に抑えて、API機能を追加する方法を解説します。CData API Serverはプログラミング不要で各種データベースからWeb APIを自動生成することができます。

#前提

- PC上でTomcatが起動する状態である。
- インターネットが使える環境である。
- もし、環境構成は難しいけれど、実際にサービス・API Serverをちょっと触ってみたい！　見てみたい！　という方はこちらまでご連絡ください。（メールアドレス：support@cdata.co.jp）

#使用するCData製品

- [CData API Server (Java版)](https://www.cdata.com/jp/apiserver/)

#Webサービス
デモ用に作成した住所検索サービスを使います。MySQLが持つ住所データを検索、閲覧、追加、削除することができます。Node.js+Express+Bootstrapで構築しました。システムの構成は下記のとおりシンプルです。
![legacySystem.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/d55499f1-de89-af66-6b66-e06a61d1cf60.png)

これにAPI Serverを加えると下記のような構成になります。WebサービスとAPI Serverは独立した機能ですので、既存のWebサービスに与える影響は少なく済みます。
![newSystem.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/8fe7de9c-f8bc-edaf-68df-d438cb0e9f5a.png)

Icons made by [Freepik](http://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/)

##画面構成

- ログイン画面
![login.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/5b702526-2fcb-c174-acec-3c4de65abd40.png)

- メイン画面
郵便番号、住所を検索、閲覧、追加、削除することができます。
![main.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/52106a48-5bab-087c-be1e-e27515039437.png)

- プロフィール画面
ユーザ名の確認、パスワードの変更を行うことができます。
![profile.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/6d0732b6-0969-72a5-036d-7ef2c9de1a80.png)

#API Serverをインストールする
今回はJava版を使用しますので、[こちら](https://www.cdata.com/jp/apiserver/download/)からパッケージをダウンロードしてください。解凍すると「.war」ファイルが得られるので、これを下記のフォルダにコピーします。

```
<TomcatRootFolder>/webapps/
```

次に「tomcat-users.xml」を開き、下記の1行を追加してください。

```
<TomcatRootFolder>/conf/tomcat-users.xml
```

```
<user name="admin" password="xxx" roles="cdata_admin,admin-gui,manager-gui,manager-status,manager-script,manager-jmx" />
```

Tomcatを起動し、ブラウザから“localhost:8080/apiserver/”にアクセスし、下記のように表示されれば成功です。
※今回はTomcatはデフォルトのまま使用しますのでポート番号は8080です。
![APIServerLogin.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/64ecfb60-4fc6-1606-bd38-141430dce83f.png)

先ほど「tomcat-user.xml」に追加した管理ユーザでログインすることができます。この管理コンソールからAPI Serverの各種操作を行うことができます。初めは「情報」タブでライセンスアクティベーションが必要な旨のメッセージが表示されます。今回は評価版を使用するので「30日の評価版をアクティベート」をクリックします。この時、ライセンスサーバーとの通信が発生するため、インターネットに接続できる環境が必要です。評価版のライセンスが取得できると「プロダクトキー」の欄に文字列が表示されます。
![APIServerActivation.png](https://qiita-image-store.s3.amazonaws.com/0/288655/d65e5cd0-60b7-0370-f576-9343fe7d97fd.png)

#APIを生成する
##接続の追加
次にAPI Serverの「設定 > 接続」タブを開き、「＋接続の追加」ボタンをクリックします。接続可能なDatabase一覧が表示されますので「MySQL」をクリックします。

下記のような画面が表示されますので、住所検索サービスが使用するMySQLの情報を入力し「変更を保存」をクリックします。

| 項目 | 設定値 |
|:-----------------|:---------------|
| 名前 | 任意 |
| Server | MySQLサーバのFQDN |
| Port | MySQLサーバのポート番号 |
| Database | MySQLのデータベース名 |
| User | MySQLのアカウント |
| Password | MySQLのパスワード |

保存後は下記のように接続が追加されたことが確認できます。
![Connection4.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/a72b4fba-c794-1148-62c0-a19719d09ec5.png)

##リソースの追加
「設定 > リソース」タブを開き、「リソースを追加...」ボタンをクリックします。
![Resource.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/49b1f0fb-136d-2e68-0481-5af225c150a6.png)

ここでは作成済みのデータ接続の一覧が表示されますので、先ほど作成した接続名を選択し「次へ」をクリックします。
![Resource2.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/cbddc54d-3002-17d0-b0b1-fb5b6d3db520.png)

次の画面では、選択したデータベースの中のテーブル一覧が表示されますので、ここから適当なテーブルを選択します。住所検索サービスで使用するテーブルは「address」のみなので、これを選択し「次へ」をクリックします。
![Resource3.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/12753f3d-b551-ec4c-4cf5-5e3d6a9b4a91.png)

最後に許可する操作と取得するフィールドを選択します。今回は操作は全て許可、フィールドも全て取得するので、デフォルトのまま「保存」をクリックします。「ユーザーの許可」もデフォルトのままです。
![Resource6.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/a13de362-a3ff-af39-8f9b-d6255a257c9a.png)

保存後は下記のようにリソースが追加されたことを確認できます。
![Resource5.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/6c0b1e3a-05ef-4160-c727-a8234637d6ed.png)

メニューの「API」をクリックすると下記のようにAPIの仕様について確認することができます。また同時に左側ペインの「リソース」の欄に追加したリソースに紐づくURLが表示されます。
![API2.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/d0c10346-870e-f6d9-4f71-0e914e3aa605.png)

この“/api.rsc/address”が生成されたデータアクセス用APIのURLです。これだけの操作でMySQLの「address」テーブルにアクセスするAPIが作られました。管理コンソール上でこのURLをクリックすると、各操作の具体例などを確認することができます。
![API3.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/ba07a23a-5ab3-308f-d4ca-8d2e1834598a.png)

データアクセス用APIの生成は以上で完了です。このようにAPIの生成自体は非常に簡単です。

#管理用API
またAPI Serverには、只今作成したデータアクセス用APIとは別に、管理用APIがあります。管理コンソールと同じことを行えますので、ここまで手動で行ってきた設定作業を自動化することができます。

##管理用APIを有効化する
デフォルトでは管理用APIはオフになっています。web.xmlファイルをエディタなどで開き、下記の1行を適当な位置に追加します。

```
<TomcatRootFolder>/webapp/apiserver/WEB-INF/web.xml
```

```xml

<security-constraint>
    <display-name>Restrict direct access to the Management API</display-name>
    <web-resource-collection>
        <web-resource-name>Management API</web-resource-name>
        <url-pattern>/admin.rsc/*</url-pattern>
        <url-pattern>/admin.rst/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>cdata_admin</role-name>  <!-- この1行 -->
    </auth-constraint>
  </security-constraint>
```

Tomcatを再起動後、先ほどと同じユーザで再度ログインし、管理APIアクセス用の「admin」ユーザを「設定 > ユーザー」タブから追加します。「権限」は全てにチェックし、その他はデフォルトのままです。
![User2_arrow.png](https://qiita-image-store.s3.amazonaws.com/0/288655/c70b746a-07e6-e4e5-f7b9-a68b11e9798c.png)

保存後は、下記のようにadminユーザーの「認証トークン」が表示されます。これで現在管理コンソールにログインしている「admin」ユーザに認証トークンを発行したわけです。管理用APIにアクセスする際はこのトークンを使用します。
![User3.png](https://qiita-image-store.s3.amazonaws.com/0/288655/bb41b791-4d34-a688-e186-f322dafefe3e.png)

##管理用APIの仕様書
ここまでの作業で「admin」ユーザは管理用APIへアクセスできるようになりました。管理コンソールにログインしたまま、ブラウザから下記のURLにアクセスしてみます。

```
http://localhost:8080/apiserver/admin.rst
```
下記のように管理用APIの仕様と操作可能なリソースを確認することができます。
![Manage.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/639c78f1-2ae5-c9cf-74b0-0ecfd4825bf5.png)

これでAPI Serverの設定は完了です。
ここまでの作業を下記にまとめます。

- 住所検索サービスが使用しているMySQLのaddressテーブルにアクセスするためのAPIを生成した
- adminユーザのみがアクセスできる管理用APIを有効化した

#住所検索サービスの拡張
データアクセス用APIを使用するには認証トークンが必要ですので、下記のように住所検索サービスの各ユーザが認証トークンを取得できるようにプロフィール画面にフォームを追加します。

変更前
![profile3.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/7c98bf92-e0e8-a6b0-b0c4-bdffd123c8aa.png)

変更後
![profile2.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/f985cbe6-ae10-f693-6aad-9ab7fd84ac46.png)

上記の画面のユーザ名「tanaka」は認証トークンを未取得なので「Web API」のフォームは空白です。ここで「認証トークン取得」をクリックすると、下記のように、管理用APIを使って生成・取得した認証トークンと共に必要な情報が表示されます。
![profile6.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/0156eb87-c25b-cabd-5454-f1a3bcc9fd2a.png)

このために、サーバサイドでは、GET/POSTリクエスト受信時に下記のような処理を実行するURL、/apiusr を1つ追加します。

- GET /apiusr

管理用API、/admin.rsc/Users にGETリクエストを送信し、取得した全ユーザの情報の中から当該ユーザの情報のみを返します。住所検索サービスでは、プロフィールページロード時に使われます。管理用APIを使用するサーバサイド(Node.js)のコードは下記のとおりです。HTTPクライアントは[httpモジュール](https://nodejs.org/api/http.html)です。

```js

var getAPIUserInfo = function(username) {
  return new Promise((resolve, reject) => {
    var conf = utils.getconf();　// 認証コード等の情報をファイルから読み出し
    var options = {
      hostname: conf.apiserver_host,
      port: parseInt(conf.apiserver_port),
      path: "/apiserver/admin.rsc/Users",
      method: "GET",
      headers: {
        "Content-Type": "application/json;charset=utf-8",
        "x-cdata-authtoken": conf.apiserver_token,
      },
    };

    var body = "";
    var req = http.request(options, (res) => {
      res.on("data", (chunk) => {
        body += chunk;
      });

      res.on("end", () => {
        var result = JSON.parse(body).value.find(v => v.UserName == username);
        if(result !== undefined) resolve(result);
        else resolve(null);
      });
    });

    req.on("error", (err) => {
      reject(err);
    });

    req.end();
  });
}
```

- POST /apiusr

管理用API “/admin.rsc/Users”にPOSTリクエストを送信し、API Serverのユーザを新規作成します。成功すると、作成されたユーザの情報が得られるので、これをそのまま返します。情報検索サービスでは「認証トークン取得」ボタンクリック時に使われます。管理用APIを使用するサーバサイド(Node.js)のコードは下記のとおりです。

```js

var createAPIUser = function(username) {
  return new Promise((resolve, reject) => {
    var conf = utils.getconf();　// 認証コード等の情報をファイルから読み出し
    var options = {
      hostname: conf.apiserver_host,
      port: parseInt(conf.apiserver_port),
      path: "/apiserver/admin.rsc/Users",
      method: "POST",
      headers: {
        "Content-Type": "application/json;charset=utf-8",
        "x-cdata-authtoken": conf.apiserver_token,
      },
    };

    var body = "";
    var req = http.request(options, (res) => {
      res.on("data", chunk => {
        body += chunk;
      });

      res.on("end", () => {
        var result = JSON.parse(body);
        if("error" in result) throw new Error(result.error.message);
        else resolve(result);
      });
    });

    req.on("error", (err) => {
      reject(err);
    });

    var postdata = JSON.stringify({
      "@odata.type":"CDataAPI.Users.rsd",
      "UserName": username,
      "Privileges": "GET,POST,DELETE",
    });

    req.write(postdata);
    req.end();
  });
}
```

住所検索サービスの拡張は以上で終了です。
このようにAPI Serverを使うことで既存のWebサービスにAPI機能をアドオンすることができます。認証トークンを発行する手続きは必要なので、Webサービスを少々修正する必要はあるかと思いますが、一からAPIを実装する手間ほどではないでしょう。
住所検索サービスのソースコードは[こちら](https://github.com/kankiry/node-express-addresslookup)から入手できます。

#ユーザに公開されたAPIを使ってみる
[POSTMAN](https://www.getpostman.com/)を使って、公開されたデータアクセス用APIを使ってみます。住所検索サービスのプロフィール画面に表示されている情報を各項目に入力して「Send」をクリックします。下記のように画面下にはレスポンスが表示され、住所データが取得できていることが分かります。

- リクエストタイプ　： GET
- URL　：　http://localhost:8080/apiserver/admin.rsc/Users/
- リクエストヘッダー

  - x-cdata-authtoken　：　認証トークン
  - Content-Type　：　application/json;charset=utf-8

![postman_user.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/66cb9be0-ebff-3722-5146-d24f755a3fa7.png)

さらに、フィルターを付加してみます。下記のように、URLのクエリストリングとして、JSON ODataフォーマットでフィルタ条件を記述します。この例では、郵便番号（フィールド名：zip）が“9800811”に一致する、という条件でデータを取得しています。

```
 http://localhost:8080/apiserver/api.rsc/address?$filter=zip eq '9800811'
```

![postman4.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/a78dde4d-ccce-c0f1-dece-e2aea1c75403.png)

#補足
管理用APIを使ってユーザを追加するリクエストを[POSTMAN](https://www.getpostman.com/)を使って見てみます。 

- リクエストタイプ　：POST
- URL　：　http://localhost:8080/apiserver/admin.rsc/Users/
- リクエストヘッダー

  - x-cdata-authtoken　：　API Server adminユーザの認証トークン
  - Content-Type　：　application/json;charset=utf-8
  
- Body（JSON形式）

  - "@odata.type" : "CDataAPI.Users.rsd"　※これは固定
  - "UserName"　：　新規に作成するユーザ名
  - "Privileges"　：　ユーザに許可する操作　※今回のWebサービスではGET/POST/DELETE
  
![postman.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/d8a25a13-8b46-6d89-1a47-56642c1a4b40.png)
![postman2.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/d89dcdfd-4868-cb0a-a92e-61467c7c4575.png)

このリクエストに対し、下記のようなレスポンスが得られます。「AuthToken」が認証トークン、「Privileges」が許可された操作の情報です。
![postman3.PNG](https://qiita-image-store.s3.amazonaws.com/0/288655/82f3fd59-eea0-96d6-5a98-eb94501237c1.png)