Delphi 10.2 Tokyo Enterprise と RAD Server で REST JSON な Web API を作る

この記事は2017年6月3日（土）に行われた [第2回 RAD Studio勉強会@Nagoya (Delphi / C++Builder](https://rad-studio-nagoya.connpass.com/event/52624/) の私のセッションのフォローアップ記事です。

Delphi 10.2 Tokyo Enterprise では Linux 向けのサーバサイドアプリケーション実装も行えるようになっています。

そしてサーバサイドの実装としては DataSnap, WebBroker, RAD Server という機能が提供されています。このうち、RAD Server では Apache , IIS で動くモジュールを作ることができます。

そこでこの記事では Delphi 10.2 Tokyo Enterprise の RAD Server で REST JSON な Web API を作ってみることにします。

# この記事の中で紛らわしい記述について
RAD Server は、もともとは EMS (Enterprise Mobility Service) という名称で提供されていた機能です。このために Delphi の IDE では大半の部分において EMS の名称がほとんどそのまま残っています。

つきましては、この記事の中で EMS という記述が出てきたら、それは RAD Server のことを指していると読み替えて頂くとよいです。

# この記事で説明を省略していること
EMSサーバの開発環境向けのセットアップ手順は、この記事では詳細について省略しています。

## Windows の開発環境内向けセットアップ

- InterBase XE7 Developer Edition を予めインストールしておく必要があります。EMSサーバは内部情報の管理データベースとしてInterBaseを使用するためです。InterBaseはIDEの追加オプションとしてインストールします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/46e67540-6cca-7725-af3f-8341f8f716d7.png)

- 開発版EMSサーバの初期設定。これはEMSサーバ向けのプロジェクトを初めてビルドする際に自動的にウィザートが走ります。

## Linux の開発環境向けセットアップ

- InterBase XE7 のインストール

ココらへんを見て頂くとよいでしょう。
http://qiita.com/mojeld/items/0c62cf45fb509e4a2ea8

- 開発版EMSサーバのインストールと初期設定

Linux向けの開発版EMSサーバは、Delphi のインストールパス
"C:\Program Files (x86)\Embarcadero\Studio\19.0\EMSServer" に tar ball とインストーラがあります。これを root 権限でインストールするだけです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/f5ae8f96-ea79-b840-331f-c1b203fc781f.png)

なお、インストールは /usr/lib/ems 配下に対して行なわれます。

また、インストールの手順については DocWiki の下記ページにも説明があります。

http://docwiki.embarcadero.com/RADStudio/Tokyo/ja/Linux_での運用環境への_EMS_サーバーまたは_EMS_コンソール_サーバーのインストール


# RAD Server 向けのプロジェクトの作成の流れ

RAD Server 向けのプロジェクト作成の流れは概ね次のようになります。

- Windows 上でEMSサーバのプロジェクトを作成して動作確認する。
- Linux での稼働も必要ならば Linux の開発環境向けにもデプロイして動作確認する。
- 運用環境向けの ApacheやIISと組み合わせて動作確認する。

このうち「運用環境向けの ApacheやIISと組み合わせて動作確認する。」は RAD Server の運用環境向けライセンスが必要となるため、ここではあくまで開発環境向けの動作確認までの範囲を書いてみます。

# Windows 上でEMSサーバのプロジェクトを作成して動作確認する。

以下の5ステップで進めます。

- 新規のプロジェクトを「EMSパッケージ」で作成する
- 必要なコンポーネント４つを配置する
- コンポーネントのプロパティを設定する
- コードを書く
- ビルドして動作確認する

## 新規のプロジェクトを「EMSパッケージ」で作成する

IDEのメニューより「ファイル」=>「新規作成」=>［その他」を選んで表示されるダイヤログにて、「Delphiプロジェクト」=>「EMSパッケージ」=>「EMS」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/27c34947-fdec-c46d-255f-84f663096d59.png)

「リソースを含むパッケージを作成する」を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/d9d73702-8e5f-6017-74c0-ad39f5063ce4.png)

「リソース名」を適当に入力して先に進みます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/4a33d47a-f139-3beb-7384-7965eefbbb39.png)

エンドポイントは今回は初期値の Get, GetItem だけが選択された状態で完了を押します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/c7076597-283b-93ac-c158-1a472a7b0ce9.png)


## 必要なコンポーネント４つを配置する

今回は以下の4つを配置してみます。

- FDConnection
- FDQuery
- FDSchemaAdapter
- FDStanStorageJSONLink

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/945a5c5f-629d-6707-c877-11748ae81bd3.png)


## コンポーネントのプロパティを設定する

### FDConnection

FDConnectionで右クリックし、メニューから「接続エディタ」を選択します。接続エディタでは下記の設定を行います。

| パラメータ | 値 |
|:--|:--|
| Database | C:\Users\Public\Documents\Embarcadero\Studio\19.0\Samples\Data\EMPLOYEE.GDB |
| User_Name | sysdba |
| Password | masterkey |
| Protocol | TCPIP |
| Server | (Delphi を実行中のPCのIPアドレス) |
| CharacterSet | UTF8 |

なお、上記の User＿Name, Password は InterBase のデフォルト値ですので、デフォルトから変えている場合は変更後の値を指定します。また、Server に Delphi を実行中のPCのIPアドレスを指定するのは、後に行う Linux 向けデプロイでデータベースのインストールを省略することを目的としています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/5de40ffe-5ebf-c409-dcaa-400ddfcfd7a9.png)

設定が完了したら接続のテストを実行して正しく接続できることを確かめます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/d4831b58-c2d4-299c-c96c-6bb69c990942.png)

さらに FDConnection のプロパティで LoginPrompt を False にしておきます。これにより、接続時の認証は接続エディタで設定済みの内容で行なわれるようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/18563e9a-d028-aa2e-0adb-e1c2aa5be0e0.png)

### FDQuery

FDQuery を右クリックして「クエリーエディタ」を開きます。クエリーエディタでは ``` select * from customer ``` を試しに実行してみることにします。すると画面下の RecordSet に結果一覧が表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/97664b28-ea15-5dea-ef2d-cb9941f4e806.png)

ではさらにクエリを書き換えて、外部からパラメータを注入できるようにしてみます。``` where CUST_NO = :CUST_NO ``` と追記して……

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/53d3fe2e-2c2d-40e9-f441-fa6ff9f5d97c.png)

「パラメータ」タブを選択し、データ型 = ftInteger, 値 = 1001 と入力して、再度実行してみると、where で絞りこまれた結果になりました。このようにしてクエリに記入した ``` :CUST_NO ``` の部分はコードから値を注入するパラメータとして利用できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/debd3349-b849-2cf9-355c-ffabea4a0a4d.png)

設定が終わったら、クエリーエディタを閉じてプロパティも設定します。SchemaAdapter のプロパティを今回配置した FDSchemaAdapter にしておきます。これにより、FDQuery と SchemaAdapter が連携するようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/ad56ba32-0762-33ca-0fbc-6859594291e8.png)


## コードを書く

コードエディタより TEMStestResource1.GetItem を探して以下のコードに差し替えます。

```delphi
procedure TEMStestResource1.GetItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
  LItem: string;
  oStr: TMemoryStream;
begin
  LItem := ARequest.Params.Values['item'];

  oStr := TMemoryStream.Create;
  try
    FDQuery1.ParamByName('CUST_NO').AsString := LItem;
    FDQuery1.Open;
    FDSchemaAdapter1.SaveToStream(oStr, TFDStorageFormat.sfJSON);
    AResponse.Body.SetStream( oStr, 'application/json; charset=utf-8', True );
  except
    oStr.Free;
    raise;
  end;
end;
```


## ビルドして動作確認する

ビルドして実行すると、今回作成した REST JSON な API がEMSサーバの開発サーバ経由で起動します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/e58872e6-22d6-78ac-70a6-7d752f613ea0.png)

今回作成したリソースは EMStest ですので、``` http://localhost:8080/EMStest/1002 ``` にアクセスしてみます。すると、次のような出力が得られます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/6b3c440f-9bde-4bed-96f9-cafaa62b8b22.png)

このようにして、CUST_NO での絞込検索を行う API を実装することができました。

なお、ここでの JSON の出力形式は、FireDAC の内部データ形式となっています。この形式は各カラムの定義や実際のレコードからなる形式のため、少々冗長な形式ではあります。しかしクライアント側もこの形式を取り扱えるコンポーネントを使って実装すれば、JSON の生成や読み込みに関する処理はほとんどコードを書くことなしに実装することができます。 


# Linux での稼働も必要ならば Linux の開発環境向けにもデプロイして動作確認する。



Linux向けにビルドする場合は、プロジェクトマネージャより「ターゲットプラットフォーム」を右クリックすると、「プラットフォームの追加」のメニューが表示されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/f0a64d01-7411-64b4-afaf-2037a3d95f9d.png)

プラットフォームの追加から「64ビット Linux」を選択すれば、Linux 向けのビルドが行えるようになります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/3fdd54f9-f616-086a-e18d-d764857084c8.png)

ただし実際のビルドでは Linux の SDK を開発環境側にキャッシュさせる必要があります。この作業は初回のビルドの時に行なわれます。このときに IDE と Linux の実機の間での通信が発生するのですが、この通信では Linux 側で PAServer （プラットフォームアシスタンスサーバ）のプロセスを実行させておく必要があります。

PAServer は Delphi の IDE に含まれていますので、これを Linux 側にコピーします。Linux のオペレーションに慣れている方なら scp, sftp を使うのがよいでしょうし、そうでない場合は USB メモリ等で受け渡すのがカンタンです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/45fb4bc5-3b6a-9a82-f236-43950a20c994.png)

では PAServer のアーカイブを展開して実行してみます。i [enter] とすると Linux 機材の IP アドレスを表示します。また v [enter] とすると、詳細表示モードに切り替わります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/e0c11730-4966-7bf5-fb84-775e339eb791.png)

ではビルドを実行してみることにしますが、Linuxのインストール時に GUI をインストールしている場合は、ビルドの前にプロジェクトのオプションで「ランチャアプリケーションを使用する」にチェックを入れておきます（GUI をインストールしていない場合はビルドして実行したのちにちょいとトリッキーな作業が発生します）。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/37cd7bd0-4a15-24b9-8c62-63b9369ff69e.png)

では F9 キーを押して「デバッガで実行」してみることにします。すると初回のビルドでは SDK のキャッシュを取得するためのウィザードが順次走ります。最初に行うのは IDE から Linux への接続プロファイルの作成です。まずはプロファイル名を入力します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/a191a4d6-0b59-3198-3cfa-3d493ccf95cc.png)

そして接続先機材のIPアドレス（またはホスト名）を入力します。PAServer 起動時に接続パスワードを設定している場合はここでも同じ接続パスワードを入力しておきます。入力したら接続テストを行い、正しく通信できることを確かめます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/b6de13fe-9699-72a9-cb2f-740d5d071a06.png)

接続プロファイルを作成したら、続いて SDK の取得が行われます。プロファイルとSDKバージョンが正しいことを確認して先に進みます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/ae224677-fd1e-f5b1-06c4-7f00c244ac72.png)

LinuxからのSDKのコピー中はこんな画面で処理が進みます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/217285c0-ca5a-14e9-897a-6cc6d1ad5e24.png)

ビルドが終わってデバッグ実行が始まると、GUI をインストールしている場合は Linux 側は次のような画面になっているはずですので、paconsole で start と入力すれば EMS サーバの開発版が 8080 で稼働開始します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/6dbe3aae-d8a6-e122-f4d6-8f9f3d940416.png)

GUI をインストールしていない場合は IDE 側は次のようになっていると思いますが、デバッグ実行をここで停止させてください。DelphiでIDEからLinux向けにPAServer経由でデプロイ、実行する場合はビルドしたアプリケーションは必然的にPAServerの子プロセスとして稼働するわけですが、GUI をインストールしていない環境では PAServerとEMS開発サーバの両方が標準入出力を取り合うので、EMS開発サーバに対して "start" のコマンドを投入するのが難しくなります。従って、デバッグ実行中のプロセスとPAServerの両方を止めた上で、EMS開発サーバを単体実行したほうが操作がカンタンになります。デバッグ実行は IDE で ctrl + F2 を押すことで強制停止できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/494b36f5-b534-0340-0f44-fa43a3083760.png)

デバッグ実行中のプロセスを止めたら PAServer にも "q" と入力して停止させます。その後、```/usr/lib/ems/EMSDevServerCommand -lPAServer/scratch-dir/kazuhiro.inoue-Ubuntu/Project1/bplProject1.so``` のように実行してEMSサーバを起動させます。EMS開発サーバのプロンプトで start と入力すれば 8080 の Listen が開始されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/f53716ed-7a1a-4568-20d0-5d6542a3b8a7.png)

さて、実際にブラウザから ``` http://linuxのIPアドレス:8080/EMStest/1003 ``` のようにアクセスすると、Windows 環境での実行と同様の結果が取得できているはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/149350/83c7e888-5880-0fc9-1a7c-ebb6ced242d9.png)


このようにして、同一のプロジェクトから Windows 向け、Linux 向けに RAD Server (EMS) のプロジェクトをビルドすることができました。なお、最終的にこれを運用環境で利用する場合には、IIS 向けや Apache 向けのラッパーモジュールと組み合わせることになります。