## 経緯 仕事でモックサーバーが必要になった。 はじめは[[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 ```