外部APIを使ったバックエンドのおすすめデバッグ手法

Webアプリケーションのバックエンドでは、多くの場合何かしらの外部APIを使っています。そのとき、どんなリクエストを送りどんなレスポンスを受け取っているのか確認したい場面は多いです。

実際にはログに出力して確認することも多いですが、ログには他の情報も出力されているので、テキストファイルから該当部分を探し出すのはちょっと手間です。また、いまいちな実装だとどこでどんな通信をしているのか分からなかったり、工数や保守の都合で既存のソースコードにログ追加をできなかったりする場面もあります。そういった場合、HTTPリクエスト・APIリクエストをキャプチャできる外付け的な仕組みがあると便利です。

この記事では、ローカルの開発環境で便利な2種類の方法を紹介します。

TL;DR

  • ログに頼らずAPIの結果を調査できると、開発で便利
  • mitmproxyを使うと、コンテナ内のHTTPリクエストを確認できる
  • Mockoonを使うと、もっと簡単にAPIリクエストを確認できる

なお、構成図などは私の開発環境 (Windows + WSL 2) を前提にしていますが、LinuxやMacでもほとんど変わりません。

想定するアプリケーション

実際にはLaravelやSymfonyなどのプロジェクトですが、仮想的な例としてこんなコードのイメージです。このアプリケーションでは、https://jsonplaceholder.typicode.com/users/1 にアクセスしてJSONを取得しています。

index.phpではGuzzleを使っているので本来であればcomposerの準備も必要ですが、本題からそれるのでここでは省略してindex.phpとDocker Composeのみを紹介します。

index.php:

<?php

require 'vendor/autoload.php';

// `/?user_id=1` のようなクエリパラメータを取得
$userId = filter_input(INPUT_GET, 'user_id', FILTER_VALIDATE_INT);
$apiUrl = 'https://jsonplaceholder.typicode.com/users/' . $userId;

// APIで、ユーザーの情報のSONを取得
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', $apiUrl);

$user = json_decode($response->getBody(), true);

?>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>blog</title>
</head>
<body>
    <h1>ユーザー情報</h1>
    <?php if ($user): ?>
        <p>ID: <?= htmlspecialchars($user['id']) ?></p>
        <p>名前: <?= htmlspecialchars($user['name']) ?></p>
        <p>ユーザー名: <?= htmlspecialchars($user['username']) ?></p>
        <p>Email: <?= htmlspecialchars($user['email']) ?></p>
    <?php else: ?>
        <p>ユーザーが見つかりませんでした。</p>
    <?php endif; ?>
</body>

compose.yaml:

services:
  app:
    image: php:8.3-apache
    ports:
      - "8000:80"
    volumes:
      - .:/var/www/html

本当に動かしたければDockerfileを用意するか、次のような操作でcomposerをセットアップする必要があります。

$ docker compose up -d
$ docker compose exec app bash

root# apt-get update && apt-get install -y git
root# curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
root# composer require guzzlehttp/guzzle

ここではPHP自体の環境構築は本題でないので、以降では index.php ではなく curl を使って動作確認していきます。

コンテナでmitmproxyを使う

まず mitmproxy とは、HTTP/HTTPSトラフィックを傍受・分析できるプロキシツールです。似た用途では Fiddler や Wireshark が有名ですが、mitmproxyは機能がシンプルなPython製のOSSとして有力です。

使用するには、Docker ComposeのYAMLを次のように変更します。

compose.yaml:

services:
  app:
    image: php:8.3-apache
    ports:
      - "8000:80"
    volumes:
      - .:/var/www/html
      - mitmproxy-data:/mitmproxy:ro
    environment:
      HTTP_PROXY: http://mitmproxy:8080
      HTTPS_PROXY: http://mitmproxy:8080
      NO_PROXY: "localhost,host.docker.internal"
    depends_on:
      - mitmproxy

  mitmproxy:
    image: mitmproxy/mitmproxy
    ports:
      - "8081:8081" # Web UI
    command: mitmweb --web-host 0.0.0.0 --web-port 8081 --listen-host 0.0.0.0 --listen-port 8080 --set web_password="password"
    volumes:
      - mitmproxy-data:/home/mitmproxy/.mitmproxy

volumes:
  mitmproxy-data:

見てのとおり mitmproxy もコンテナとして起動します。http://localhost:8081 でWeb UIにアクセスできます。command に書いてあるように “password” でログインできます。

HTTP_PROXY と HTTPS_PROXY の環境変数を設定しておくと、curlやGuzzleなどがプロキシを使ってくれます。

しかしこれだけではHTTPSのリクエストがエラーとなります

root@1a70316ea901:/var/www/html# curl https://jsonplaceholder.typicode.com/users/1
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

HTTPSの通信をプロキシ経由で送信するためには、mitmproxyの証明書をコンテナにインストールする必要があります。そこで、コンテナ内で次の操作をします。

なお、このコマンドは php:8.3-apache のベースイメージがDebianなのでそれに合わせています。CentOS系や Alpine Linuxなど、他のベースイメージを使う場合は適宜変更する必要があります。

openssl x509 -in /mitmproxy/mitmproxy-ca-cert.pem -inform PEM -out /usr/local/share/ca-certificates/mitmproxy.crt
update-ca-certificates

compose.ymlで /mitmproxy をボリュームマウントしているのはこのためでした。これでやっとコンテナ内でcurlを使ってHTTPSリクエストが成功します。

root@1a70316ea901:/var/www/html# curl https://jsonplaceholder.typicode.com/users/1
{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "Sincere@april.biz",
  "address": {
    "street": "Kulas Light",

mitmproxy の Web UI を見ると、どんなリクエストを送信してどんなレスポンスを受信したのか、詳細な情報を確認できます。

ちなみに、docker compose exec app bash で証明書をインストールする方法だと、コンテナをビルドするごとに同じ手順が必要です。entrypointのスクリプトに含めるなどしても良いですが、私は常にプロキシを使うわけではないので都度手動でやっています。

Mockoonを使う方法

mitmproxyとは違うアプローチとして、Mockoonを使う方法が簡単でおすすめです。

MockoonはAPIのモックサーバーを作成するGUIツールです。Mockmoonには、モックが定義されていないリクエストを外部のAPIに転送するProxy機能があります。この機能を使うと、モックを作成しなくても実在するAPIに送信したリクエストを確認することができます。

この方法ではプロキシや証明書のためのコンテナ設定が不要な代わりに、接続先のURLを変更したり、ホストOSにMockoonをインストールしたりする必要があります。

まずは、Mockmoonをインストールしてください。Mockoonはmitmproxyのようにコンテナとして起動することもできるそうですが、設定のJSONファイルなどが必要なのでいずれにしてもデスクトップアプリが必要です。wingetを使ったり、インストーラーを公式サイトからダウンロードしたりしてインストールします。

https://mockoon.com/download

Mockmoonを起動したら “New local environment” から新しい環境を作成します。ここでは適当に “blog” という名前にしておきます。

Proxyタブを開き、”Enable proxy mode” のチェックボックスをONにします。そして、 “Target URL” に実際にアクセスしたい外部APIのURLを設定します。ここでは https://jsonplaceholder.typicode.com (※スクリーンショットではexample.com) を指定します。

最後に、左上の三角形のスタートボタンをクリックしてサーバーを起動します。

そこで、コンテナ内で次のようにcurlを実行してみるとログが残っています

root@1a70316ea901:/var/www/html# curl http://host.docker.internal:3004/users/1
{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "Sincere@april.biz",
  "address": {
    "street": "Kulas Light",

ここで、アクセス先のURLが http://host.docker.internal:3004 になっているのに注目してください。元のURL https://jsonplaceholder.typicode.com にアクセスすると、その通信はMockoonを経由せず直接APIにリクエストを送信してしまいます。

mitmproxyはHTTPのプロキシサーバーとして機能していました。一方でMockoonはあくまでAPIのモックを作るツールなので、接続先のURLを元々のAPIのエンドポイントからMockoonのエンドポイント(localhost:3004)に変更する必要があります。さらに、コンテナ内からホストOSのポートにアクセスしたいので localhost ではなく host.docker.internal を指定しています。

まとめ

mitmproxyはHTTPの通信を傍受するためのツールで、MockmoonはダミーのAPIを動かすためのツールと、方向性が異なります。

総合的には、Mockoonを使う方が簡単なので多くの場面でおすすめです。しかし、デバッグ対象のAPIが不特定な場合や、接続先のURLを変更できない場合などはmitmproxyの方が便利です。それぞれの特徴に応じた使い分けが必要ですね。

mitmproxy

  • Pros
    • HTTP/HTTPSのリクエスト・レスポンスを詳細にキャプチャできる
    • 全てのHTTP通信を傍受できるため、API以外の通信も確認可能
    • 実装(主にGuzzleやcurlの場合)によっては、アプリ側のコード変更なしで使える
    • 上の方法では、ホストOSへのインストールなどが不要
  • Cons
    • HTTPSの場合、証明書のインストールが煩雑
    • プロキシの設定も必要
    • プロキシを使わない通信(オプション未指定のfile_get_contents関数で通信した場合など)はキャプチャできない
    • 関係ない通信(composer installで発生するリクエストなど)もプロキシされてしまう場合がある

Mockoon

  • Pros
    • 証明書やプロキシなど、コンテナ側の設定が不要で分かりやすい
    • 特定のリクエストに特定のレスポンスを返すなど、本来のモック機能も併用できる
  • Cons
    • アプリでのAPIエンドポイントを、Mockoonのエンドポイントに変更する必要がある
    • 複数のAPIをデバッグしたいとき、その分の設定が必要になる
    • Mockoonでのサーバー起動が必要 (ただしコンテナで起動可能かも)

注意点として、mitmproxyはプロキシされた全ての通信をキャプチャできますが、プロキシが使われない通信はキャプチャされません。例えば、環境変数 HTTPS_PROXY を設定しておくとcurlやPHPのGuzzleはプロキシを使ってくれますが、PHPの file_get_contents 関数は明示しないとプロキシを使いません。プロキシというのは環境によっては設定がいろいろ面倒なことも多く、そういった場合はMockoonを使った方がおそらく簡単です。

ちなみに、上ではmitmproxyをコンテナ内のみで使うようにしましたが、ホストOS (Windows)でプロキシとして使うことも可能です。たとえばpipでmitmproxyをインストールしておけばPowerShellなどで mitmweb を実行するだけでプロキシを起動できます。Windowsの設定でプロキシを localhost:8080 に設定したり証明書をインストールしたりすると、あらゆるHTTPS通信を覗き見ることができます。ブラウザをちょっと操作するだけで大量のログが残ってしまうため開発には向きませんが、インストールしたアプリが不審な通信をしていないかなどの調査には便利です。

最後に

今回の記事では、最近の私がAPIをデバッグする際に使っている方法を紹介しました。他にもっとスマートなやり方もあるかもしれませんが、mitmproxyもMockoonもどちらも知っておいて損はしないツールだと思いますのでご参考になれば幸いです

上部へスクロール