はじめに
前回の記事 SharePoint Webhook検証(1): Webhook作成&通知フロー では、
SharePoint Webhookの仕組みおよび作成&通知フローを確認しました。
今回は、Webhookレシーバーの要件と実装について確認します。
Webhookの作成&通知フローのおさらい
- Webhook作成フロー
- Webhook通知フロー
Webhookレシーバー(Receiver)の役割
上のシーケンス図で示しているように、Webhookレシーバーは、SharePointとユーザ・管理者の間で仲介役を担います。
SharePointからの変更通知を、一旦Webhookレシーバーが受け取り、管理者に情報を渡すなど制御を行います。
Webhookレシーバーの要件
Webhookレシーバーは、以下二つの機能を持つ必要があります。
- Webhook作成時の検証機能
- SharePointからのリクエストから
validationToken
の値を取得 -
validationToken
の値をレスポンスに含め、SharePointに返す
- SharePointからのリクエストから
- Webhook通知を受け取りハンドリング
- SharePointからの更新通知を受信したら、レスポンスを返す
- 受信イベントの内容を記録または管理者に渡す
Webhookレシーバーの実装
Webhookレシーバーの実装はいくつか方法が考えられます。
- Microsoftチュートリアルでは、Visual Studio Web APIを使用しています
- 本記事では、NGINX(エンジンエクス)を使用して、Webhookレシーバーを作成します
Wikipediaから、NGINXとは、
- フリーかつオープンソースなWebサーバ
- 処理性能・高い並行性・メモリ使用量の小ささに焦点を当てて開発された
- HTTP, HTTPS, SMTP, POP3, IMAPのリバースプロキシの機能を持つ
- ロードバランサ、HTTPキャッシュなどの機能も持つ
NGINXを用いてWebhookレシーバーを作成する手順
-
必要なNGINX拡張モジュール
-
ngx_http_js_module.so
- ロケーションと変数ハンドラーの実装にnjsスクリプト言語を使用するため必要
-
ngx_http_echo_module.so
- リクエストのヘッダーとボディ情報をNGINXアクセスログに記録するため必要
-
ngx_http_js_module.so
-
Webhookレシーバーの処理を行うnjsスクリプト
import qs from 'querystring';
// リクエストheaderをJSON形式で取得
function stringify_headers(r) {
return JSON.stringify(r.headersIn);
}
// リクエストから検証トークンを取得
function get_validation_token(r) {
var token = r.args["validationToken"];
return token ? token : '';
}
export default { stringify_headers, get_validation_token };
-
nginx.conf
にWebhookレシーバーに必要な設定を追加
... ...
# 動的モジュールをロード
load_module modules/ngx_http_echo_module.so;
load_module modules/ngx_http_js_module.so;
... ...
http {
js_import webhook.js;
js_set $headers_json webhook.stringify_headers;
js_set $validation_token webhook.get_validation_token;
# アクセスログにリクエストのヘッダーとボディを記録
log_format ltsv escape=none 'request_method:$request_method\t'
'uri:$request_uri\t'
'status:$status\t'
'request_header:$headers_json\t'
'request_body:$request_body';
access_log /var/log/nginx/access.log ltsv;
... ...
server {
location /sharepoint-webhook {
# SharePointからのWebhook作成リクエストの場合、検証トークンが含まれる
if ( $validation_token ) {
return 200 $validation_token;
}
# SharePointからのWebhook通知リクエストの場合、ヘッダーとボディをログに記録
echo_read_request_body;
echo $request_body;
}
Webhook作成の動作確認
- Graph APIを使用して、
Subscription
作成リクエストを送る
POST https://graph.microsoft.com/v1.0/subscriptions
ボディ:
{
"changeType": "updated",
"notificationUrl": "https://test.net/sharepoint-webhook",
"resource": "/sites/test.sharepoint.com,zzzzzz/drives/b!xxxxxxxx/root",
"expirationDateTime": "2023-06-01T18:23:45.9356913Z",
"clientState": "yyyyyyyyyyyyy"
}
- SharePointからのNGINXへ検証リクエストが届く
NGINXのアクセスログ
request_method:POST
uri:/sharepoint-webhook?validationToken=Validation%3a+Testing+client+application+reachability+for+subscription+Request-Id%3a+yyyyyyyy
status:200
request_header:{
"Accept":"text/plain",
"Content-Type":"text/plain; charset=utf-8",
"Host":"test.net",
"Content-Length":"0",
"Connection":"Keep-Alive"
}
request_body:
- NGINXからSharePointへ検証トークンを含めたレスポンスを返却
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
"id": "aaaaaaaaaaaaaaaaaaa",
"resource": "/sites/test.sharepoint.com,zzzzzz/drives/b!xxxxxxxx/root",
"applicationId": "bbbbbbbbbb",
"changeType": "updated",
"clientState": "yyyyyyyyyyyyy",
"notificationUrl": "https://test.net/sharepoint-webhook",
"notificationQueryOptions": null,
"lifecycleNotificationUrl": null,
"expirationDateTime": "2023-06-01T18:23:45.9356913Z",
"creatorId": "cccccccccccccccccccccc",
"includeResourceData": null,
"latestSupportedTlsVersion": "v1_2",
"encryptionCertificate": null,
"encryptionCertificateId": null,
"notificationUrlAppId": null
}
Webhook通知の動作確認
-
SharePoint上でWebhook対象のドキュメントに対して、コピーなどの操作を行う
-
SharePointから変更通知がNGINXに届く
NGINXのアクセスログ
request_method:POST
uri:/sharepoint-webhook
status:200
request_header:{
"Content-Type":"application/json; charset=utf-8",
"Host":"test.net",
"Content-Length":"xxx",
"Connection":"Keep-Alive"
}
request_body:{
"value":[{
"subscriptionId":"xxxxxxxxxxxxxxx",
"clientState":"yyyyyyyyyyyyy",
"resource":"/sites/test.sharepoint.com,zzzzzz/drives/b!xxxxxxx/root",
"tenantId":"kkkkkkkkkkkkkk",
"resourceData":null,
"subscriptionExpirationDateTime":"2023-06-01T18:23:45.9356913+00:00",
"changeType":"updated"}]
}
おわりに
SharePointのWebhookレシーバーについて検証してみました。
SharePointドキュメントに対して、予期せぬ追加・変更が行われた場合など、
Webhookを用いて、ほぼリアルタイムで監視できるようになりそうです。