# webhookの配信処理

Webhook 配信をリッスンして応答するコードを記述する方法について説明します。

## はじめに

Webhook を作成する際は、URL を指定し、イベントの種類をサブスクライブします。 webhook がサブスクライブされているイベントが発生すると、 GitHub は、指定した URL にイベントに関するデータを含む HTTP 要求を送信します。 その URL で Webhook 配信をリッスンするようにサーバーが設定されている場合は、受信したときにアクションを実行できます。

この記事では、サーバーが Webhook 配信をリッスンして応答できるようにコードを記述する方法について説明します。 ローカル サーバーとしてコンピューターまたは codespace を使用して、コードをテストします。

## セットアップ

Webhook をローカルでテストするために、webhook プロキシ URL を使用して、webhook を GitHub からコンピューターまたはコードスペースに転送できます。 この記事では、smee.io を使用して Webhook プロキシ URL を指定し、Webhook を転送します。

### Webhook プロキシ URL を取得する

1. ブラウザーで <https://smee.io/> にアクセスします。
2. **新しいチャンネルの開始**をクリックします。
3. \[Webhook Proxy URL] (Webhook プロキシ URL) の下にある完全な URL をコピーします。 この URL は次の設定ステップで使用します。

### Webhook を転送する

1. [smeeクライアント](https://www.npmjs.com/package/smee-client) がまだインストールされていない場合は、ターミナルで次のコマンドを実行します。

   ```shell copy
   npm install --global smee-client
   ```

2. smee.io から転送された Webhook を受信するには、ターミナルで次のコマンドを実行します。
   `WEBHOOK_PROXY_URL` を、以前の Webhook プロキシ URL に置き換えます。

   ```shell copy
   smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
   ```

   次のような出力が表示されます。`WEBHOOK_PROXY_URL` は Webhook プロキシ URL です:

   ```shell copy
   Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook
   Connected WEBHOOK_PROXY_URL
   ```

   パスが `/webhook` でポートが `3000` であることに注意してください。 これらの値は、後で Webhook 配信を処理するコードを記述するときに使用します。

3. Webhook のテスト中は、これを実行し続けます。 Webhook の転送を停止する場合は、 <kbd>Ctrl</kbd>+<kbd>C</kbd> を押します。

### webhook を作成する

1. 次の設定を使用して Webhook を作成します。 詳しくは、「[webhookの作成](/ja/enterprise-server@3.22/webhooks/using-webhooks/creating-webhooks)」をご覧ください。

   * URL には、前の Webhook プロキシ URL を使用します。
   * コンテンツ タイプを選択するオプションがある場合は、JSON を使用します。

## Webhook の配信を処理するコードを記述

Webhook の配信を処理するには、次のコードを記述する必要があります。

* サーバーを初期化して、Webhook URL への要求をリッスンします。
* 要求から HTTP ヘッダーと本文を読み取る
* 要求に応答して目的のアクションを実行

サーバーで実行できる任意のプログラミング言語を使用できます。

次の例では、Webhook 配信を受信したときにメッセージを出します。 ただし、 GitHub API への要求や Slack メッセージの送信など、別のアクションを実行するようにコードを変更できます。

* [Ruby の例](#ruby-example)
* [JavaScript の例](#javascript-example)

### Ruby の例

この例では、Ruby gem の Sinatra を使用してルートを定義し、HTTP 要求を処理します。 詳しくは、[Sinatra の README](https://github.com/sinatra/sinatra#readme) をご覧ください。

#### Ruby の例: 依存関係のインストール

この例を使用するには、Ruby プロジェクトに Sinatra gem をインストールする必要があります。 たとえば、これは [Bundler](https://bundler.io/) を使って行うことができます。

1. Bundler がまだインストールされていない場合は、ターミナルで次のコマンドを実行します。

   ```shell copy
   gem install bundler
   ```

2. アプリに Gemfile がまだない場合は、ターミナルで次のコマンドを実行します。

   ```shell copy
   bundle init
   ```

3. アプリに Gemfile.lock がまだない場合は、ターミナルで次のコマンドを実行します。

   ```shell copy
   bundle install
   ```

4. ターミナルで次のコマンドを実行して Sinatra gem をインストールします。

   ```shell copy
   bundle add sinatra
   ```

#### Ruby の例: コードの記述

次のような内容の Ruby ファイルを作成します。 Webhook がサブスクライブしているイベントの種類と、webhook の作成時に送信`ping`GitHub イベントを処理するようにコードを変更します。 この例では `issues` イベントと `ping` イベントを処理します。

```ruby copy annotate
# These are the dependencies for this code. You installed the `sinatra` gem earlier. For more information, see [Ruby example: Install dependencies](#ruby-example-install-dependencies). The `json` library is a standard Ruby library, so you don't need to install it.
require 'sinatra'
require 'json'

# The `/webhook` route matches the path that you specified for the smee.io forwarding. For more information, see [Forward webhooks](#forward-webhooks).
#
# Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.
post '/webhook' do

  # Respond to indicate that the delivery was successfully received.
  # Your server should respond with a 2XX response within 30 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.
  status 202

  # Check the `X-GitHub-Event` header to learn what event type was sent.
  # Sinatra changes `X-GitHub-Event` to `HTTP_X_GITHUB_EVENT`.
  github_event = request.env['HTTP_X_GITHUB_EVENT']

  # You should add logic to handle each event type that your webhook is subscribed to.
  # For example, this code handles the `issues` and `ping` events.
  #
  # If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  # For example, this code handles the `opened` and `closed` actions for the `issue` event.
  #
  # For more information about the data that you can expect for each event type, see [AUTOTITLE](/webhooks/webhook-events-and-payloads).
  if github_event == "issues"
    data = JSON.parse(request.body.read)
    action = data['action']
    if action == "opened"
      puts "An issue was opened with this title: #{data['issue']['title']}"
    elsif action == "closed"
      puts "An issue was closed by #{data['issue']['user']['login']}"
    else
      puts "Unhandled action for the issue event: #{action}"
    end
  elsif github_event == "ping"
    puts "GitHub sent the ping event"
  else
    puts "Unhandled event: #{github_event}"
  end
end
```

#### Ruby の例: コードのテスト

Webhook をテストするのに、コンピューターまたは codespace をローカル サーバーとして機能するように使用できます。 これらのステップで問題が生じた場合は、「[トラブルシューティング](#troubleshooting)」を参照してください。

1. Webhook が転送されていることを確認します。 Webhook が転送されなくなった場合は、「[Webhook の転送](#forward-webhooks)」のステップをもう一度実行します。

2. 別のターミナル ウィンドウで、次のコマンドを実行して、コンピューターまたは codespace でローカル サーバーを起動します。
   `FILE_PATH` を前のセクションのコードが格納されているファイルへのパスに置き換えます。 前のステップで Webhook 転送に指定したポートと `PORT=3000` が一致していることに注意してください。

   ```shell copy
   PORT=3000 ruby FILE_NAME
   ```

   "Sinatra has taken the stage on 3000" のような出力が表示されるはずです。

3. Webhook をトリガーします。 たとえば、`issues` イベントをサブスクライブするリポジトリ Webhook を作成した場合は、リポジトリでイシューを開きます。 以前の Webhook 配信を再配信することもできます。 詳しくは、「[Webhook の再配信](/ja/enterprise-server@3.22/webhooks/testing-and-troubleshooting-webhooks/redelivering-webhooks)」をご覧ください。

4. smee.io の Webhook プロキシ URL に移動します。 トリガーまたは再配信したイベントに対応するイベントが表示されるはずです。 これは、 GitHub が指定したペイロード URL に Webhook 配信を正常に送信したことを示します。

5. `smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000` を実行したターミナル ウィンドウに、`POST http://127.0.0.1:3000/webhook - 202` といった表示が出るはずです。 これは、smee が Webhook をローカル サーバーに正常に転送したことを表します。

6. `PORT=3000 ruby FILE_NAME` を実行したターミナル ウィンドウに、送信されたイベントに対応するメッセージが表示されるはずです。 たとえば、上記のコード例を使用し、 `ping` イベントを再配信した場合は、"ping イベントを送信GitHub " と表示されます。 また、Sinatra が自動的に出力する他の文も表示される場合もあります。

7. 両方のターミナル ウィンドウで、<kbd>Ctrl</kbd>+<kbd>C</kbd> を押してローカル サーバーを停止し、転送された Webhook のリッスンを停止します。

コードをローカルでテストしたら、運用環境で Webhook を使用するように変更を加えることができます。 詳細については、「[次のステップ](#next-steps)」を参照してください。 コードのテストで問題が発生した場合は、「[トラブルシューティング](#troubleshooting)」の手順を試してください。

### JavaScript の例

この例では、Node.js と Express ライブラリを使用してルートを定義し、HTTP 要求を処理します。 詳細については、[expressjs.com](https://expressjs.com) を参照してください。

GitHubの Octokit.js SDK を使用する例については、[webhook イベントに応答するGitHub アプリの構築](/ja/enterprise-server@3.22/apps/creating-github-apps/writing-code-for-a-github-app/building-a-github-app-that-responds-to-webhook-events) を参照してください。

この例では、お使いのコンピューターまたは codespace で Node.js バージョン 12 以降と npm バージョン 6.12.0 以降が実行されている必要があります。 詳細については、「 [Node.js](https://nodejs.org)」を参照してください。

#### JavaScript の例: 依存関係のインストール

この例を使用するには、Node.js プロジェクトに `express` ライブラリをインストールする必要があります。 次に例を示します。

```shell copy
npm install express
```

#### JavaScript の例: コードを記述する

次の内容を持つJavaScriptファイル を作成します。 Webhook がサブスクライブしているイベントの種類と、webhook の作成時に送信`ping`GitHub イベントを処理するようにコードを変更します。 この例では `issues` イベントと `ping` イベントを処理します。

```javascript copy annotate
// You installed the `express` library earlier. For more information, see [JavaScript example: Install dependencies](#javascript-example-install-dependencies).
const express = require('express');

// This initializes a new Express application.
const app = express();

// This defines a POST route at the `/webhook` path. This path matches the path that you specified for the smee.io forwarding. For more information, see [Forward webhooks](#forward-webhooks).
//
// Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.
app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {

  // Respond to indicate that the delivery was successfully received.
  // Your server should respond with a 2XX response within 30 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.
  response.status(202).send('Accepted');

  // Check the `x-github-event` header to learn what event type was sent.
  const githubEvent = request.headers['x-github-event'];

  // You should add logic to handle each event type that your webhook is subscribed to.
  // For example, this code handles the `issues` and `ping` events.
  //
  // If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  // For example, this code handles the `opened` and `closed` actions for the `issue` event.
  //
  // For more information about the data that you can expect for each event type, see [AUTOTITLE](/webhooks/webhook-events-and-payloads).
  if (githubEvent === 'issues') {
    const data = request.body;
    const action = data.action;
    if (action === 'opened') {
      console.log(`An issue was opened with this title: ${data.issue.title}`);
    } else if (action === 'closed') {
      console.log(`An issue was closed by ${data.issue.user.login}`);
    } else {
      console.log(`Unhandled action for the issue event: ${action}`);
    }
  } else if (githubEvent === 'ping') {
    console.log('GitHub sent the ping event');
  } else {
    console.log(`Unhandled event: ${githubEvent}`);
  }
});

// This defines the port where your server should listen.
// 3000 matches the port that you specified for webhook forwarding. For more information, see [Forward webhooks](#forward-webhooks).
//
// Once you deploy your code to a server, you should change this to match the port where your server is listening.
const port = 3000;

// This starts the server and tells it to listen at the specified port.
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
```

#### JavaScript の例: コードをテストする

Webhook をテストするのに、コンピューターまたは codespace をローカル サーバーとして機能するように使用できます。 これらのステップで問題が生じた場合は、「[トラブルシューティング](#troubleshooting)」を参照してください。

1. Webhook が転送されていることを確認します。 Webhook が転送されなくなった場合は、「[Webhook の転送](#forward-webhooks)」のステップをもう一度実行します。

2. 別のターミナル ウィンドウで、次のコマンドを実行して、コンピューターまたは codespace でローカル サーバーを起動します。
   `FILE_PATH` を前のセクションのコードが格納されているファイルへのパスに置き換えます。

   ```shell copy
   node FILE_NAME
   ```

`Server is running on port 3000`という出力が表示されます。

1. Webhook をトリガーします。 たとえば、`issues` イベントをサブスクライブするリポジトリ Webhook を作成した場合は、リポジトリでイシューを開きます。 以前の Webhook 配信を再配信することもできます。 詳しくは、「[Webhook の再配信](/ja/enterprise-server@3.22/webhooks/testing-and-troubleshooting-webhooks/redelivering-webhooks)」をご覧ください。
2. smee.io の Webhook プロキシ URL に移動します。 トリガーまたは再配信したイベントに対応するイベントが表示されるはずです。 これは、 GitHub が指定したペイロード URL に Webhook 配信を正常に送信したことを示します。
3. `smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000` を実行したターミナル ウィンドウに、`POST http://127.0.0.1:3000/webhook - 202` といった表示が出るはずです。 これは、smee が Webhook をローカル サーバーに正常に転送したことを表します。
4. `node FILE_NAME` を実行したターミナル ウィンドウに、送信されたイベントに対応するメッセージが表示されるはずです。 たとえば、上記のコード例を使用し、 `ping` イベントを再配信した場合は、"ping イベントを送信GitHub " と表示されます。
5. 両方のターミナル ウィンドウで、<kbd>Ctrl</kbd>+<kbd>C</kbd> を押してローカル サーバーを停止し、転送された Webhook のリッスンを停止します。

コードをローカルでテストしたら、運用環境で Webhook を使用するように変更を加えることができます。 詳細については、「[次のステップ](#next-steps)」を参照してください。 コードのテストで問題が発生した場合は、「[トラブルシューティング](#troubleshooting)」の手順を試してください。

## トラブルシューティング

テスト ステップで説明された予想される結果が表示されない場合は、次の試してください。

* Webhook が Smee.io の Webhook プロキシ URL を使用しているか確認します。 Webhook プロキシ URL の詳細については、「[Webhook プロキシ URL の取得](#get-a-webhook-proxy-url)」を参照してください。 Webhook 設定の詳細については、「[webhookの作成](/ja/enterprise-server@3.22/webhooks/using-webhooks/creating-webhooks)」を参照してください。
* 使用するコンテンツ タイプを選択できる場合は、Webhook で JSON コンテンツ タイプが使用されていることを確認します。 Webhook 設定の詳細については、「[webhookの作成](/ja/enterprise-server@3.22/webhooks/using-webhooks/creating-webhooks)」を参照してください。
* smee クライアントとローカル サーバーの両方が動作していることを確認します。 これらのプロセスは、2 つの個別のターミナル ウィンドウで実行されます。
* サーバーが、smee.io が Webhook を転送しているのと同じポートをリッスンしていることを確認します。 この記事のすべての例では、ポート3000を使用しています。
* smee.io が webhook を転送しているパスが、コードで定義されているルートと一致していることを確認します。 この記事のすべての例では、`/webhooks` パスを使用します。
* smee クライアントとローカル サーバーを実行しているターミナル ウィンドウでエラー メッセージを確認します。
* webhook の配信がトリガーされたことを確認するには、 GitHub を確認します。 詳しくは、「[Webhook配信の確認](/ja/enterprise-server@3.22/webhooks/testing-and-troubleshooting-webhooks/viewing-webhook-deliveries)」をご覧ください。
* smee.io の Webhook プロキシ URL を確認します。 トリガーまたは再配信したイベントに対応するイベントが表示されるはずです。 これは、 GitHub が指定したペイロード URL に Webhook 配信を正常に送信したことを示します。

## 次のステップ

この記事では、Webhook 配信を処理するコードを記述する方法を示しました。 また、コンピューターまたはコードスペースをローカル サーバーとして使用し、 GitHub から smee.io 経由でローカル サーバーに webhook 配信を転送することで、コードをテストする方法についても説明しました。 コードのテストが完了したら、コードを変更し、サーバーにコードをデプロイすることができます。

### コードを変更する

この記事では、Webhook 配信を受信したときにメッセージを出力する基本的な例を示しました。 コードを変更して、他のアクションを実行することもできます。 たとえば、次を実行するようにコードを変更できます。

* GitHub API への要求を行う
* Slack でメッセージを送信
* ログイベント
* 外部プロジェクト管理ツールの更新

### 配信元が GitHub であることを確認します。

Webhook の配信を処理するコードでは、配信をさらに処理する前に、配信が GitHub から行われるかどうかを検証する必要があります。 詳しくは、「[Webhook 配信を検証する](/ja/enterprise-server@3.22/webhooks/using-webhooks/validating-webhook-deliveries)」をご覧ください。

### コードをサーバーにデプロイする

この記事では、コードの開発時にコンピューターまたは codespace をサーバーとして使用する方法について説明しました。 コードを運用環境で使用する準備ができたら、コードを専用サーバーにデプロイする必要があります。

その場合は、サーバーがリッスンしているホストとポートを反映するようにコードを更新する必要があります。

### Webhook URL を更新する

GitHubから webhook トラフィックを受信するようにサーバーを設定したら、webhook 設定の URL を更新します。 新しい URL のパス部分と一致するように、コードが処理するルートを更新する必要がある場合があります。 たとえば、新しい webhook URL が`https://example.com/github-webhooks`である場合、これらの例のルートを `/webhooks` から`/github-webhooks` に変更する必要があります。

運用環境では Webhook を転送するのに smee.io を使用しないでください。

### ベスト プラクティスに従う

Webhook についてはベスト プラクティスに従うようにする必要があります。 詳しくは、「[Webhook の使用に関するベスト プラクティス](/ja/enterprise-server@3.22/webhooks/using-webhooks/best-practices-for-using-webhooks)」をご覧ください。

## 参考資料

* [webhook イベントに応答するGitHub アプリの構築](/ja/enterprise-server@3.22/apps/creating-github-apps/writing-code-for-a-github-app/building-a-github-app-that-responds-to-webhook-events)
* [Webhook の使用に関するベスト プラクティス](/ja/enterprise-server@3.22/webhooks/using-webhooks/best-practices-for-using-webhooks)