基本的なサーバーサイドAPI連携の手順

このガイドでは、基本的なサーバーサイドAPI連携の実現方法について説明します。

これにより、ウェブサイト上のユーザーが現在のURLにアクセスする権限を持っているかどうかが確認され、権限がない場合は待機室へリダイレクトされます。

このガイドは開発者を対象としています。APIなどの知識があることを前提としています。

この連携機能は、CrowdHandler APIの公開リソースを、公開鍵を使用して利用します。無料プランを含め、どのプランでも利用可能です。

1. APIへの接続

まず、公開鍵が必要です。APIの概要を参照して、APIキーを取得してください。

2. 初めての電話をかける

POST /requests/

ここでは、API呼び出しのデモにPostmanを使用しています。Postmanは、API呼び出しを行ったり、パラメータの送信をテストしたり、統合を構築する際にどのようなレスポンスが返ってくるかを確認したりするのに最適なツールです。

キーの送信にはBasic Authを使用しています。Postmanでは、「認証」タブでこの情報を一度入力するだけで、以降のすべてのリクエストでその設定が適用されます。キーは「ユーザー名」フィールドに入力します。「パスワード」は無視されます。

認証ヘッダーの設定が完了したので、まずは https://api.crowdhandler.com/v1/requestsに対してPOSTリクエストを送信することから始めます。このリクエストは、サイト上のURLに対して新しいリクエストを開始し、そのユーザーがアクセス権を持つべきか、あるいはリダイレクトされるべきかを確認するために使用されます。

このPOSTリクエストでは、以下のJSONペイロードが期待されます:

{
    "url": "https://test.crowdhandler.com/some-url",
    "ip": "127.0.0.1",
    "agent": "Just testing",
    "lang": "Just testing"
}

これらのパラメータについて順を追って見ていきましょう:

  • url- これは、ユーザーがアクセスしようとしているURLです。このURLをどこから取得するかは、アプリケーション、言語、フレームワークによって異なります。CGI変数である場合もあれば、何らかのフロントコントローラーやルーターを使用している場合もあります。URLはプロトコルを含めて完全な形式である必要があります。CrowdHandlerはHTTPSのみをサポートしています。
  • ip- これはユーザーのリクエストのIPアドレスです。この情報をどこから取得するかは、使用している言語やフレームワークによって異なります。IPアドレスは必須です。これは不正防止やボット検知のために使用されます。
  • agent- これはユーザーエージェント文字列です。これも必須です。User-AgentHTTPヘッダーから取得する必要があります。
  • lang- これはユーザーが希望する言語です。これはオプションのパラメータであり、不正なエージェントの特定に役立ちます。HTTPヘッダーの「Accept-Language」から取得できます。

問題がなければ、ご指定いただいたURLが「待機室」で保護されている場合、次のような応答が表示されるはずです:

{
    "result": {
        "status": 1,
        "token": "tok_6ihQ7XNw0GmS",
        "title": "Queen Victoria Hall",
        "position": 648,
        "promoted": 0,
        "urlRedirect": null,
        "onsale": "2020-10-21T15:11:11Z",
        "message": "Our website is very busy right now. Sorry to keep you waiting, we will redirect you as soon as possible.",
        "slug": "queen-victoria-hall",
        "priority": null,
        "priorityAvailable": 0,
        "logo": null,
        "responseID": "1dc0784ea988c4139f406061f39edd39",
        "ttl": 0
    }
}

出力内容を順に確認していきます:

  • トークンとは、ユーザーを識別するための識別子です。当初はトークンを持っていなかったため、トークンを必要としないPOSTリクエストを送信しました。その結果、最初のリクエストで新しいトークンを取得できたのです。
  • 「promoted」は、私たちが注目している主な属性です。値が 0 の場合、このトークンを持つユーザーは、指定された URL に対してキューに並ぶ必要があります。値が 1 の場合、ユーザーはキューに並ぶ必要がありません。このトークンの「promoted」の値は 0 であり、このユーザーにキューに並ぶ必要があることを示しています。
  • slug:slugは待合室のアドレスを示します。待合室の完全なアドレスはhttps://wait.crowdhandler.com/your-slugとなります
  • responseID は、受け取ったばかりのレスポンスを識別するための一意の ID です。後で、CrowdHandler API を使用してこのリクエストのパフォーマンスを記録する際に、これを使用する場合があります。
  • その他のプロパティも興味深いものであり、トラブルシューティングやデバッグに役立つ可能性がありますが、単純な統合を行う場合には、これらを使用する必要はありません。他の理由でこれらのプロパティを使用したい場合は、APIドキュメントでその意味を確認してください。

したがって、もし当社の統合機能がこの特定のレスポンスを受け取った場合、そのユーザーを待合室にリダイレクトしたいと考えます: https://wait.crowdhandler.com/queen-victoria-hall

何か違うものが見えますか?

上記の模範解答とは異なる回答が出ている可能性があります。回答が著しく異なる場合は、表示されている内容に応じて、以下のいずれかの問題が発生している可能性があります:

  • エラーですか?これは、リクエストパラメータに何らかの問題があることを示しています。エラーメッセージには、どこが間違っているのかが記載されているはずです。
  • 待合室の情報がない?どの待合室の範囲にも該当しないURLを送信した場合、トークンは受け取れますが、`promoted`の値は1となるものの、待合室に関する情報は返ってきません。テストを行うために、包括的な待合室を設定してみることをお勧めします。
  • トークンがない?CrowdHandlerアカウントに登録されていないドメイン名のURLを使って呼び出しをテストしても、プロモート値として「1」が返されます(当社はそのドメインの保護責任を負わないため、当社としては問題ないとみなします!)。ただし、トークンは取得できません。
  • ステータス 3 が表示されている場合はお使いの IP アドレスがブロックされていることを意味します。これは、同じ IP アドレスから多数の公開 API リクエストを行った場合に発生することがあり、連携機能のテスト中によく見られる現象です。管理パネルにログインし、「ドメイン」→「IP」からご自身の IP アドレスを確認し、その IP アドレスのステータスを「無視」に変更してください。

さて、最初の呼び出しを終えました。出力結果がどうなっているか、それにどう対応すべきかも分かっています。この情報の具体的な扱い方については、次の呼び出しについて説明した後で話しましょう。

3. 2回目の電話

GET /requests/:token

最初の例では、ユーザートークンがありませんでした。このPOST呼び出しはトークンを受け取るのではなく、トークンを返します。 つまり、サイト上のユーザーがトークンを保有していない場合に実行すべきリクエストがこれです。しかし、ユーザーが待機室から通過してサイトにアクセスした場合は、トークンを保有しています。その場合は、そのトークンを使用し、ユーザー認証のために保持しておく必要があります。なぜなら、新しいトークンを発行してしまうと、そのユーザーは再びキューに戻ってしまう可能性が高いからです。

トークンはどこで見つかりますか?

CrowdHandlerがユーザーを貴社のサイトにリダイレクトする際、URLの末尾にトークンが追加されます。これは「ch-id」というクエリ文字列パラメータとして指定されます。例:https://yoursite.com/your-url?ch-id=tok_S0mET0k3n。したがって、そのURLパラメータにトークンが含まれているかどうかを確認する必要があります。トークンが見つかった場合は、POSTリクエストではなくGETリクエストを実行してください。

でも、それだけじゃないんです!

その後、ユーザーはウェブサイト上の他の多くのリンクをクリックすることになります。そのため、ユーザーが再び「待合室」に戻ってしまうのを防ぐには、トークンが返ってきた際にセッション Cookie を設定し、URL 内に ch-id が見つからない場合はその Cookie を確認する必要があります。それでも見つからない場合は、POST リクエストを送信してください。

トークンには有効期限がありますか?

その通りです。しかし、無効なトークンを渡した場合、CrowdHandlerはそのトークンを無視し、トークンが設定されていないかのようにリクエストを処理した上で、リクエストの検証を行うとともに新しいトークンを返します。つまり、トークンは自動的に破棄されるため、トークンの追跡や有効期限の管理について心配する必要はありません。しかし、その一方で、返ってくるトークンが必ずしも送信したトークンそのものであるとは限らないということです。 送信したトークンよりも、受け取ったトークンを信頼する必要がありますので、毎回クッキーを設定してください

まずクッキーを確認すべきでしょうか、それともURLパラメータを確認すべきでしょうか?

URLパラメータについて。複雑な状況下では、ユーザーが待機室で新しいトークンを取得しているにもかかわらず、サイト側には古いクッキーが保存されたままになっている可能性があります。ユーザーが待機室から転送されてきた場合、URLには新しいトークンが含まれているため、以前にクッキーとして設定した、古くなっている可能性のあるトークンよりも、その新しいトークンを信頼する必要があります。

もちろん、ch-id パラメータがないことを、既存のクッキーを削除する指示として解釈してはいけません。ですから、よく確認してください。

さて……その電話は:

ペイロードはPOSTリクエストの場合と同じですが、今回はGETリクエストであるため、パラメータは生のJSONではなく、GETパラメータとして送信します。URLが変更されています。今回は、トークンがURLの一部となっています。

このレスポンスは、POSTリクエストと非常によく似ています。ただし、今回は受け取ったトークンが、送信したものと同じであることが確認できます。このデータに基づいて、この連携が下すべき判断は、POSTリクエストの場合とまったく同じです。

まとめとして

POSTリクエストとGETリクエストは、同じ情報を返します。ユーザーからトークンを受け取っていない場合はPOSTリクエストを実行し、受け取った場合はGETリクエストを実行します。これらのリクエストは順番に実行するものではなく、どちらでもよい選択肢です。どちらの場合でも、レスポンスの形式は同じであり、実行される処理も同一になります。

ユーザーがサイト内のさまざまなURLにアクセスする際に、その都度認証を行えるように、トークンをCookieまたはセッションオブジェクトに保存する必要があります。

トークン Cookie やセッションの設定には注意が必要です。待機中のユーザーがサイト内の他の URL にアクセスしようとする可能性があり、その際にそのユーザーをキューの最後尾に戻してしまうようなことは避けたいからです。このため:

  1. このユーザーが待機すべきかどうかが判明するまで待つのではなく、できるだけ早い段階でトークンを保管することをお勧めします。
  2. セッションの有効期限には注意が必要です。アプリケーションのセッションストアのタイムアウトが20分に設定されていたとしても、ユーザーが待合室に何時間も留まる可能性があります。ユーザーがブラウザを閉じるまで有効期限が切れないセッションクッキーを採用するのが賢明でしょう。永続的なクッキーを採用すれば、さらに良いかもしれません。

4. 最初の電話をかけるタイミング

CrowdHandlerの目的は、サイトを過度な負荷から保護することです。検証チェックを行う目的は、サイト上のユーザーが実際にその時点でそのURLにアクセスすべきであるかどうかを確認することにあります。したがって、コードの実行パスにおいて、可能な限り早い段階でこれらの呼び出しを行う必要があります。その目的は、サーバーの実行時間とリソースを節約することにあります。URLリクエストが判明し、呼び出しが可能になった時点で、直ちに実行する必要があります。

5. 結果の扱い方

  • 「promoted」が 1 の場合、チェックコードを終了し、通常のアプリケーションフローに戻ります。通常どおり URL を返してください。
  • もし「promoted」が 0 の場合、 スラグを使用してユーザーを待機室にリダイレクトします。「user」が正当なボットである場合は、302 HTTP リダイレクトを使用してください。これにより、URL のリダイレクトが一時的なものであることをボットに伝えます。
  • 不正な形式のレスポンスが返ってきた場合や、API呼び出しが失敗した場合は…… CrowdHandler には多層的な保護機能がありますが、そこには このような現象が発生する原因と、それに対する対応策について説明します。データセンター内やインターネットのバックボーンで一時的なネットワーク障害が発生している可能性があります。API呼び出しの処理が失敗した場合、正当な結果の解析処理が失敗した場合、あるいは評価の 昇進した 値が失敗した場合は、これを例外として扱い、適切に対処する必要があります。具体的な対処方法は、あなた次第です:
    • 「失敗時のリダイレクト」では、特に指定がない限り、ユーザーを待機列に入れるべきであると想定しています。標準の待機室のスラグが分かっている場合(安定したスラグを持つデフォルトの「キャッチオール」があるかもしれません)、ユーザーをそこに送ることができます。分からない場合は、ユーザーを https://wait.crowdhandler.comに誘導できます。そこでは、ユーザーには汎用テンプレートが表示され、システムは定期的に状況を確認し、通常サービスが再開された時点で、ユーザーを元のサイトまたは適切な待機ルームへ誘導します。
    • 失敗時の対応- API呼び出しが失敗した場合でも、ユーザーにページへのアクセスを許可するかどうかを決定できます。これは、サイトへのトラフィックがどれほど頻繁に集中するか、またそのパターンがどれほど予測可能かによって異なります。CrowdHandlerを、イベントごとの待機室を設けた、時折の計画的な利用のみを目的として導入している場合は、CrowdHandler APIから適切な形式の応答が得られない場合でも、ユーザーを信頼する方が望ましいかもしれません。

6. ユーザーのリダイレクト

ユーザーをリダイレクトする際は、リダイレクト先URLに以下のURLエンコードされたパラメータを指定する必要があります。

  1. ch-id: APIから返ってきたトークン。
  2. url:ユーザーがリクエストしたURL。クエリ文字列のパラメータが重要である場合(マーケティングやトラッキングの目的であれ、製品IDなどに使用している場合であれ)、それらもURLパラメータの一部として確実に渡されるようにする必要があります。
  3. ch-public-key:不正な形式のレスポンスを受け取った場合や、レスポンスが返ってこない場合など、ユーザーの待機室のスラグがわからない場合は、このパラメータを使用して、スラグの代わりに公開鍵を送信することができます。これにより、CrowdHandlerのセーフティネット待機室が、その鍵を使用して正しいスラグを検索し、適切に処理を行うことが可能になります。

7. ラストコール。ページのパフォーマンスの記録

PUT /responses/:id

ページのパフォーマンスをログに記録することで、CrowdHandlerはドメインのパフォーマンスを監視できるようになります。これは、トラフィックが集中している状況を監視する際に役立ち、自動調整機能を有効にする上で不可欠です。

この呼び出しはPUTリクエストです。そして、新しいリソース/responses/:idに対して行われます。このIDは、最初に実行した呼び出しの結果として、responseIDとして提供されました。

この呼び出しのペイロードは、次のようなものです:

{
    "code": 200,
    "time": 2000
}
  • code は、ユーザーに配信しているページの HTTP レスポンスコードです。通常は 200 ですが、HTTP エラーコードを捕捉できる場合は、そのコードを送信してください。これにより、CrowdHandler はウェブサイトが正常に動作していない*状況を特定しやすくなり、オートチューンが適切な措置を講じることが可能になります。このパラメータを省略した場合、CrowdHandler はデフォルトで 200 を設定します。
  • 時間 は、ページの読み込みにかかる時間をミリ秒単位で表したものです。これは次の式で計算できます:
    • 依頼を最初に受け取った際に、時間をかけて(a)

    • このPUT (b) を送信する直前に、少し時間をとって(b)

    • a から b を差し引き、その時間差がミリ秒(1000分の1秒)単位で表されるようにする。

      これは一見単純そうに見えますが、かなり注意を払う必要があります。言語やフレームワークによって、時間を微細な単位で計測する方法が異なり、VMを使用しているかベアメタルサーバーを使用しているか、あるいはプロセッサの種類によっても、差異やパフォーマンス上の問題が生じる可能性があります。一部のフレームワークには、ページのパフォーマンスを計測するための専用のAPIが用意されています。自信がある場合は、CrowdHandlerに非常に正確な測定値を指定することができます。自信がない場合は、このパラメータを単に省略してください。その場合、CrowdHandlerは最初のリクエストとその後のPUTリクエストの間の時間に基づいて、独自に計算を行います。ページ読み込み時間の推定値は、ネットワーク遅延の影響を多少受ける可能性があり、精度も低くなりますが、ヘルスチェックを行う上では十分な情報であり、計算を誤った場合に提供されかねない著しく不正確な値よりははるかに優れています。

* エラーコードを含むレスポンスは、最大応答時間を超えるリクエストと同様に、CrowdHandler によって処理されます。したがって、500 エラーを非常に高速に返している場合でも、エラーの数が許容可能な割合の閾値を超えていると、autotune は、それらのリクエストが許容可能なページ読み込み閾値を超えて応答しているかのように、トラフィックを抑制します。

いつ電話をかけるべきか。

最初の呼び出しはできるだけ早い段階で行うべきですが、この呼び出しは、ユーザーにページを配信し終えた後、できるだけ遅い段階で実行する必要があります。また、当たり前のように聞こえるかもしれませんが、この呼び出しは、実際にそのURLを配信した場合にのみ実行してください。ユーザーを待機ページにリダイレクトした場合は、この呼び出しを完全にスキップする必要があります。したがって、最初の呼び出しの後、リダイレクトを送信するか、ページを配信するか、そしてパフォーマンスを記録するかという条件分岐が行われます。

8. すべてをまとめる

以下の擬似コード**では、これらすべてを統合して、シンプルでありながら完全な統合例を示しています。これまでの長々とした説明よりも、この擬似コードの方が概念をより素早く理解できると思いますが、それでも文脈を理解するためには注釈を参照してください。

chKey = <your public key>
timeA = Timer.getTimeInMs()
request = Controller.getRequest()
response = Controller.startRresponse()
trustOnFail = true

if request.urlParams["ch-id"]
    # token in query string. We want to set cookie so we recognise this user
    response.cookies["ch-id"] = request.urlParams["ch-id"]
    # and we'll strip ch-id param and redirect to discourage token sharing
    cleanParams = request.urlParams.unset("ch-id")
    response.redirect(request.url, cleanParams, 302)
    response.end
else
    # set up the API gateway
    crowdhandler = HttpAPI.create(baseURL="https://api.crowdhandler.com/v1/", authentication=chKey)
    params = {"url": request.url, "ip": request.host, "agent": request.userAgent, "lang": request.acceptLang}
    # look for crowdhandler token in the cookie
    token = request.cookies["ch-id"]

    # check this user's request
    try
        if token
            # user has token
            result = crowdhandler.get("requests/"+token, params)
        else
            # user needs new token
            result = crowdhandler.post("requests/", params)
        end
    catch
        # api call failed!
        if trustOnFail
            # we do nothing
            break
        else
            # we send user to waiting room
            queryParams = {"url": request.url, "ch-id": token, "ch-public-key": chKey}
            response.redirect("https://wait.crowdhandler.com?", queryParams, 302)
            response.end
        end
end

# if we got here, we have a valid response.
# It's possible that the API has responded with a new token.
token = response.token
response.cookies["ch-id"] = token

if result.promoted
    # this token is good for this url
    # render your page / hand back control / you do you
    response.html = "Your Content"
    response.end
    # all done? Now to log the performance
    time = timeA - Timer.getTimeinMs()
    crowdhandler.put("/responses/"+result.responseID, {"code": 200, "time": time})
else
    # the user with this token needs to wait
   response.redirect("https://wait.crowdhandler.com"+result.slug+"?", {"url": request.url, "ch-id": token}, 302)
end

** これは、読みやすい2つの言語であるRubyとPythonの「混血児」のようなもので、API呼び出し、リクエストデータの取得、レスポンスの送信を行うための、架空のシンプルなAPIを備えています。その真意を、皆さんが行間から読み取っていただければ幸いです。

完全に動作するコードライブラリについては、当社のPHP SDKをご覧ください。