## 経緯
仕事でモックサーバーが必要になった。
はじめは[[Killgrave]]を使っていい感じだったのだが、[[XML]]リクエストの要件を満たすことができずに困っていた。
調べていたところ[[Mockoon]]が非常に良さそうだったので試してみることに。
## 前提と要件
今回の要件は以下。結論から言うと **すべての要件を満たす。**
### 必須
- [[WSL]]配下の[[Ubuntu]]環境
- [[Docker]]がインストールされている
- モックサーバー構築情報が[[Git]]で管理
- リクエストの内容をレスポンスに反映できる
- [[XML]]のリクエストおよびレスポンスに対応
- [[XML]]のリクエストに応じて異なるレスポンスを返却できる
- 仕事で使える
### あると嬉しい
- リクエストの回数によって異なる結果を返却できる (1回目と2回目で結果を変える)
- リクエストの内容をinputに、レスポンスの内容を動的に設定できる
- [[GUI]]が使える
## インストール
以下のチュートリアルを参考に進める。
<div class="link-card">
<div class="link-card-header">
<img src="https://mockoon.com/favicon-32x32.png" class="link-card-site-icon"/>
<span class="link-card-site-name">mockoon.com</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Mockoon - Create your first mock API with Mockoon</p>
</div>
<div class="link-card-description">
Use Mockoon to create your first mock API server in no time and generate fake realistic JSON body fo...
</div>
</div>
<img src="https://mockoon.com/images/tutorials/tutorial-getting-started.png" class="link-card-image" />
</div>
<a href="https://mockoon.com/tutorials/getting-started/"></a>
</div>
[[Ubuntu]]なので[[Snap]]を使うのが楽そう。
```console
$ sudo snap install mockoon
mockoon 6.0.1 from Guillaume Monnet (255kb) installed
```
## モックサーバーを起動してみる。
まずは[[Mockoon]]を起動する。
```console
/snap/mockoon/current/mockoon
```
案内に従っていくと、サンプルのモックサーバーが開かれる。
![[Pasted image 20231202214727.png]]
サーバーをスタートしてみる。
![[Pasted image 20231202214957.png]]
`/users`というAPIがあるようだ。
![[Pasted image 20231202215306.png]]
早速リクエストしてみると、Unauthorized errorに。
```console
$ curl "localhost:3000/users"
{
"error": "Unauthorized"
}%
```
どうやら [[Authorizationヘッダー]] がないと ( = nullだと) どのAPIでも認証エラーになるルールが先頭にあるようだ。
![[Pasted image 20231202215439.png]]
定義されたレスポンス。先ほどのものと一致する。
![[Pasted image 20231202215436.png]]
[[Authorizationヘッダー]]をつけてリクエストしたところ、大量のユーザー情報が返ってきた。
![[Pasted image 20231202215758.png]]
この大量のユーザーはどこで定義されたのだろうか...。以下の`Data`内で選択された`User`について。
![[Pasted image 20231202215306.png]]
よく見ると `Data` タブの中で[[Faker]]が使われていそう。
## 自分でJSONモックサーバーを作ってみる
先ほどはデモ用に用意されたモノをそのまま使っただけなので、これからは実際に欲しいものを作ってみる。
![[Pasted image 20231203181650.png]]
`~/work/mockoon-sandbox`配下に`api.json`をつくる。
![[Pasted image 20231203181938.png]]
作成された[[JSON]]は以下。これを[[Git]]管理していくことになる。
> [!code]- `api.json`
>
> ```json
> {
> "uuid": "c0c628ad-3efc-47c5-9717-e87184f64135",
> "lastMigration": 32,
> "name": "Api",
> "endpointPrefix": "",
> "latency": 0,
> "port": 3001,
> "hostname": "",
> "folders": [],
> "routes": [
> {
> "uuid": "b66f4803-3016-4410-91b6-0e257c54e29c",
> "type": "http",
> "documentation": "",
> "method": "get",
> "endpoint": "",
> "responses": [
> {
> "uuid": "f008384d-e057-4bf2-805f-11593c56e410",
> "body": "{}",
> "latency": 0,
> "statusCode": 200,
> "label": "",
> "headers": [],
> "bodyType": "INLINE",
> "filePath": "",
> "databucketID": "",
> "sendFileAsBody": false,
> "rules": [],
> "rulesOperator": "OR",
> "disableTemplating": false,
> "fallbackTo404": false,
> "default": true,
> "crudKey": "id",
> "callbacks": []
> }
> ],
> "responseMode": null
> }
> ],
> "rootChildren": [
> {
> "type": "route",
> "uuid": "b66f4803-3016-4410-91b6-0e257c54e29c"
> }
> ],
> "proxyMode": false,
> "proxyHost": "",
> "proxyRemovePrefix": false,
> "tlsOptions": {
> "enabled": false,
> "type": "CERT",
> "pfxPath": "",
> "certPath": "",
> "keyPath": "",
> "caPath": "",
> "passphrase": ""
> },
> "cors": true,
> "headers": [
> {
> "key": "Content-Type",
> "value": "application/json"
> }
> ],
> "proxyReqHeaders": [
> {
> "key": "",
> "value": ""
> }
> ],
> "proxyResHeaders": [
> {
> "key": "",
> "value": ""
> }
> ],
> "data": [],
> "callbacks": []
> }
> ```
基本は[[GUI]]からのみ編集するので中身を知る必要はない。が、中身を知っていると機能の全容がイメージしやすくなるので損はない。
## `/animals`でJSONを返す
まずはシンプルに[[JSON]]のGETから。`/animals`というパスで以下の[[JSON]]を返却するようにしてみる。
```json
{
"animals": [
{
"id": 1,
"name": "タツヲ"
},
{
"id": 2,
"name": "マサハル"
},
{
"id": 3,
"name": "みみぞう"
}
]
}
```
[[Mockoon]]では以下のように設定していく。
![[Pasted image 20231203182924.png]]
- ① pathの指定
- ② pathの説明
- ③ レスポンスをべた書き(inline)ではなく、ファイルから取得する
- ④ レスポンスファイルの指定 ~ 相対パス (絶対パスでもOK)
- ⑤ `application/json`として返却するのに必要なチェック
③について、今回はべた書きでも問題なかったが、今後設定数が増えるにつれてファイルサイズが肥大化するため、別出しを推奨する。また、この設定だと **ファイルの中身だけバージョン管理せず動的に変更したり、レスポンスを[[Neovim]]で編集** することが可能になる。
これで `http://localhost:3001/animals` にリクエストすると、レスポンスが返ってくる。
## クエリパラメータで返却値を変える
[[クエリパラメータ]]によって返却する[[JSON]]を変更してみる。
`animal1.json`
```json
{
"id": 1,
"name": "タツヲ"
}
```
`animal2.json`
```json
{
"id": 2,
"name": "マサハル"
}
```
Multiple responses機能を使う。
<div class="link-card">
<div class="link-card-header">
<img src="https://mockoon.com/favicon-32x32.png" class="link-card-site-icon"/>
<span class="link-card-site-name">mockoon.com</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Mockoon - Define multiple responses for each route</p>
</div>
<div class="link-card-description">
Multiple responses can be defined for each route with different body, headers and status. Learn how ...
</div>
</div>
<img src="https://mockoon.com/images/og-image.png" class="link-card-image" />
</div>
<a href="https://mockoon.com/docs/latest/route-responses/multiple-responses/"></a>
</div>
複数レスポンスを設定する方法が少し分かりにくいので注意。
![[Pasted image 20231203214855.png]]
Rulesタブでそのレスポンスを返す場合の条件を指定する。今回は[[クエリパラメータ]] `id`の値によって変える。
![[Pasted image 20231203214958.png]]
### どのRulesにも一致しない場合は404エラーを返す
何もしないと1つ目のレスポンスがdefault responseとして扱われ、どのRulesにも一致しない場合はこのレスポンスを返すようになる。
![[Pasted image 20231203215556.png]]
つまり `http://localhost:3001/animal?id=3` は `animal1.json` を返すことになる。
これを回避するにはdefaultのレスポンスをRulesのない404にすればよい。
![[Pasted image 20231203220259.png]]
真面目にやるなら400エラーも考慮すべきだが、話が大きくなるのでここでは省略する。
また、[[Mockoon]]が返すデフォルトの404でよければ、[[Fallback mode (Mockoon)|Fallback mode]]のオプションを有効にしてもよい。これなら専用のレスポンスを作成する必要はない。
![[Pasted image 20231203220518.png]]
## リクエストするたびにレスポンスが変わるようにする
全く同じリクエストでも、1度目と2度目でレスポンスを変えたいときには[[Sequencial route response (Mockoon)|Sequencial route response]]モードを有効にする。
![[Pasted image 20231203220910.png]]
これを有効にするとRulesが無視されるようになってしまうが、リクエストするたびにレスポンスが定義した順に返却される。最後までいくと一番最初に戻る。
- `Response 1 (200)`
- `Response 2 (200)`
の場合だと以下のようになる。
- 1回目 `Response 1 (200)`
- 2回目 `Response 2 (200)`
- 3回目 `Response 1 (200)`
- 4回目 `Response 2 (200)`
・
・
左隣には[[Random route response (Mockoon)|Random route response]]モードへの切替オプションも存在するが、利用頻度としては[[Sequencial route response (Mockoon)|Sequencial route response]]モードの方が高いだろう。
## リクエストパラメータをレスポンスに使う
リクエストパラメータ、具体的には[[パスパラメータ]]や[[クエリパラメータ]]などをレスポンスにそのまま使いたいケースはそれなりに存在する。
例えば `POST http://localhost/auth/:id` という認証APIがあり、以下のようなレスポンスを返すとする。
```json
{
"id": <パスパラメータの:idの値>,
"status": "ok"
}
```
リクエストされうる`id`のパターンだけレスポンスをつくるという方法もあるが、今回のケースでは[[Request helper (Mockoon)|Request helper]]を使うとスマートに実現できる。`urlParam`という変数を使う。
<div class="link-card">
<div class="link-card-header">
<img src="https://mockoon.com/favicon-32x32.png" class="link-card-site-icon"/>
<span class="link-card-site-name">mockoon.com</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Mockoon - Create dynamic responses with templating request helpers</p>
</div>
<div class="link-card-description">
Create dynamic fake mock data for your mock environments with Mockoon's templating request helpers. ...
</div>
</div>
<img src="https://mockoon.com/images/og-image.png" class="link-card-image" />
</div>
<a href="https://mockoon.com/docs/latest/templating/mockoon-request-helpers/#urlparam"></a>
</div>
新たなRouteとレスポンスを定義する。Rulesは省略。
![[Pasted image 20231203224308.png]]
③で指定した`:id`の値を、④の`"id"`に指定するのがポイント。この設定でリクエストすると、以下のように[[パスパラメータ]]が反映されたレスポンスを取得できる。
```console
$ curl -X POST "http://localhost:3001/auth/180702"
{
"id": 180702,
"status": "ok"
}
```
## リクエストパラメータからスマートにレスポンスファイルをマッピングする
実際にモックサーバーをつくるとき、リクエストパラメータの値部分のみが変化するレスポンスに出会うのは稀だ。大半がそれ以外の要素も連動して変化する。たとえば、[[#クエリパラメータで返却値を変える]] で紹介した以下のケース。
`animal1.json`
```json
{
"id": 1,
"name": "タツヲ"
}
```
`animal2.json`
```json
{
"id": 2,
"name": "マサハル"
}
```
2つならまだいいが、これが5や10になったとき、その数だけレスポンスの設定を追加するのは間違いなく面倒だ。レスポンスの[[JSON]]はその数だけ準備する必要があるのは分かるが、振り分けは自動にやってほしい。
[[File input templating (Mockoon)|File input templating]]という機能でこの面倒を解決できる。リクエストパラメータの内容からファイルパスを動的に変更できるのだ。
<div class="link-card">
<div class="link-card-header">
<img src="https://mockoon.com/favicon-32x32.png" class="link-card-site-icon"/>
<span class="link-card-site-name">mockoon.com</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Mockoon - Mockoon file serving documentation</p>
</div>
<div class="link-card-description">
Serve files as body for your mock API, use the templating system and set relative or absolute paths ...
</div>
</div>
<img src="https://mockoon.com/images/og-image.png" class="link-card-image" />
</div>
<a href="https://mockoon.com/docs/latest/response-configuration/file-serving/#file-input-templating"></a>
</div>
もはやRulesはいらない。`queryParam`という変数を使って、`id`の値をファイル名に差し込むだけだ。
![[Pasted image 20231203225118.png]]
以下のようなレスポンスファイルを追加したら `http://localhost/animal?id=3`でレスポンスを得られる。しかも、保存コマンドも再起動も不要だ。気が付いたらそこに...!!
`animal3.json`
```json
{
"id": 3,
"name": "みみぞう"
}
```
## XMLでリクエスト/レスポンス
[[JSON]]はほとんどのモックサーバーがサポートしているものの、[[XML]]に対応しているモックサーバーは意外と少ない。しかし、[[Mockoon]]は[[XML]]にも対応している。
<div class="link-card">
<div class="link-card-header">
<img src="https://mockoon.com/favicon-32x32.png" class="link-card-site-icon"/>
<span class="link-card-site-name">mockoon.com</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Mockoon - XML support for entering requests</p>
</div>
<div class="link-card-description">
Create realistic mock APIs and generate fake data with Mockoon's templating and rules system support...
</div>
</div>
<img src="https://mockoon.com/images/og-image.png" class="link-card-image" />
</div>
<a href="https://mockoon.com/docs/latest/response-configuration/xml-support/"></a>
</div>
仕組みとしては[[XML]]のリクエストを[[xml-js]]を使って[[JSON]]にパースし、そのプロパティを使って他機能と繋ぐ感じだ。
レスポンスは以下のようなシンプルな[[XML]]を考える。
```xml
<?xml version="1.0" encoding="utf-8"?>
<animal id="<リクエストされたid>">
<status>ok</status>
</animal>
```
### リクエストの設定
リクエストの設定は不要。[[リクエストヘッダー]]の[[Content-Type]]に`application/xml`や`text/xml`が指定されていれば、[[Request helper (Mockoon)|Request helper]]の機能を使って[[XML]]の値にアクセスできる。
### レスポンスの設定
[[xml-js]]の流儀に従うため、パスの間違いに注意。今回のケースだと`animal`タグの`id`属性にアクセスするので `animal._attributes.id`となる。
```xml
<?xml version="1.0" encoding="utf-8"?>
<animal id="{{body 'animal._attributes.id'}}">
<status>ok</status>
</animal>
```
![[Pasted image 20231203232746.png]]
### リクエストする
[[curl]]でPOSTリクエストをしてみる。
```xml
curl -X POST "http://localhost:3001/xml/animal" -H "Content-type: text/xml" \
-d '<?xml version="1.0" encoding="utf-8"?>
<animal id="180702"></animal>'
```
レスポンスのid部分にちゃんと返却された。
```xml
<?xml version="1.0" encoding="utf-8"?>
<animal id="180702">
<status>ok</status>
</animal>
```
なお、レスポンスのHeadersで[[Content-Type]]を設定しておかないと、グローバル設定の`application/json`が利用されるので注意。
![[Pasted image 20231203232951.png]]
もちろん、[[XML]]でも[[File input templating (Mockoon)|File input templating]]が使える。
![[Pasted image 20231203233317.png]]
## CLIで利用する
[[GUI]]を使わなくても[[Mockoon CLI]]を利用すれば、モックサーバーを起動できる。
```console
$ npm install -g @mockoon/cli
```
[[JSON]]をいじれば理論上は[[GUI]]がなくても[[Mockoon]]の全機能を利用可能。ただ、[[JSON]]定義は公開されてなさそうなので[[GUI]]を使った方がいいと思う。
### Dockerを利用する
CIなどを考えると[[Docker]]を使うのがポータビリティがあってよい。[[Docker Compose]]の設定ファイルを書く。
<div class="link-card">
<div class="link-card-header">
<img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/>
<span class="link-card-site-name">GitHub</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">mockoon/packages/cli at main · mockoon/mockoon</p>
</div>
<div class="link-card-description">
Mockoon is the easiest and quickest way to run mock APIs locally. No remote deployment, no account r...
</div>
</div>
<img src="https://opengraph.githubassets.com/6134e727a668aef5806650582f80b547045c62662dcdadbb3c3beafbe2df09a9/mockoon/mockoon" class="link-card-image" />
</div>
<a href="https://github.com/mockoon/mockoon/tree/main/packages/cli#docker-compose"></a>
</div>
`healthcheck`は任意。
`docker-compose.yml`
```yaml
services:
mock-server:
image: mockoon/cli:latest
command: ["--data", "mock/api.json", "--port", "3001"]
ports:
- 3001:3001
volumes:
- .:/mock:readonly
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3001/animals || exit 1"]
interval: 30s
timeout: 5s
retries: 2
start_period: 10s
```
`volumes`は`api.json`以外にレスポンスデータファイルもあるため、カレントディレクトリ全体を`/mock`に読み込み専用でマウントしている。
```console
docker compose up -d --wait
```
アクセスできればOK。あとは停止。
```console
docker compose down
```