GinハンドラーでGo SDKを使用する

著者:solvethree読了時間:1分

共有のsolvethreeクライアントを1つ使用し、c.Request.Context() から派生した期限付きの子コンテキストを Solve に渡します。対象設定とAPIキーはサーバー側に保持し、タスクを作成する前にアプリケーションの呼び出し元を認証してください。

このページの内容

依存関係をインストールして対象を設定する

  1. 1.

    Goモジュールを作成して依存関係をインストールする

    現在のGinリリースにはGo 1.25以降を使用してください。solvethree SDK自体はGo 1.22+を必要とします。独自のモジュールパスを使い、go mod init でアプリケーションのモジュールを初期化します。クイックスタートに従ってGinをインストールし、go get で solvethree.com/[email protected] を追加してください。公式SDKは、ドメイン上でホストされている公開モジュールプロキシを通じて配信されます。配信元はsolvethree.comです。

  2. 2.

    サーバーの認証情報を設定する

    SOLVETHREE_API_KEY にワークスペースのAPIキーを設定し、INTEGRATION_KEY に内部アプリケーションが X-Integration-Key で送信するシークレットを設定します。これらは別々の認証情報です。アプリケーションの呼び出し元にsolvethreeのAPIキーは必要ありません。

  3. 3.

    固定の対象を設定する

    CAPTCHA_SITE に対象のホスト名を、SOLVETHREE_PROXY に解決に使用するプロキシを設定します。まずワークスペースに有効なサイトプロファイルを作成してください。サイトプロファイルの設定を参照してください。このサンプルでは Action を login に固定しているため、統合で別のアクションを使用する場合は値を変更してください。ホスト名とアクションを設定しても、プロファイルは作成されません。

認証付きGinルートを追加する

クライアントは一度だけ作成され、並行リクエストでも安全に使用できます。ルートはsolvethreeを呼び出す前に、内部アプリケーションの認証情報を確認します。呼び出し元からリクエストIDは受け取りますが、任意の対象やプロキシは受け取りません。

Gin · 公式Go SDKで上限を設けて解決
package main

import (
    "context"
    "errors"
    "log"
    "net/http"
    "os"
    "time"
    "github.com/gin-gonic/gin"
    solvethree "solvethree.com/sdk"
)

func main() {
    client,err:=solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"),nil)
    if err!=nil { log.Fatal(err) }
    secret:=os.Getenv("INTEGRATION_KEY")
    if secret=="" { log.Fatal("Set INTEGRATION_KEY") }
    router:=gin.New()
    router.Use(gin.Recovery())
    router.POST("/captcha/solve",func(c *gin.Context){
        if c.GetHeader("X-Integration-Key")!=secret { c.Status(http.StatusUnauthorized);return }
        input:=solvethree.TaskRequest{Site:os.Getenv("CAPTCHA_SITE"),Action:"login",
            Proxy:os.Getenv("SOLVETHREE_PROXY"),RequestID:c.GetHeader("X-Request-ID")}
        ctx,cancel:=context.WithTimeout(c.Request.Context(),180*time.Second);defer cancel()
        result,err:=client.Solve(ctx,input,solvethree.SolveOptions{})
        if err!=nil {
            var failed *solvethree.TaskFailedError
            var recovery *solvethree.Error
            if errors.As(err,&failed) { c.JSON(502,gin.H{"taskId":failed.TaskID,"errorCode":failed.ErrorCode});return }
            if errors.As(err,&recovery) { c.JSON(502,gin.H{"requestId":recovery.RequestID,"taskId":recovery.TaskID});return }
            c.JSON(400,gin.H{"error":"Invalid task input"});return
        }
        // Return private solution data only to the authorized application caller.
        c.JSON(200,result)
    })
    log.Fatal(router.Run("127.0.0.1:3000"))
}

タスクには Site、Action、単一の Proxy を指定します。reCAPTCHAの要件に従い、Type と URL は未設定のままにし、デフォルトのEnterprise v3モードを使用します。

明示的なコンテキストの期限により、ハンドラーの待機時間は180秒に制限されます。クライアントが切断すると、それより早くキャンセルされる場合があります。HTTPサーバーとリバースプロキシは、このリクエスト時間を許容する必要があります。許容できない場合は、この同期ルートを開いたままにせず、作成と取得を別々の操作にしてください。

リスナーはループバックにバインドします。このルートを、アプリケーションの既存の内部認証およびデプロイ構成に統合してください。呼び出し元を認証せずに、有料の解決エンドポイントを公開しないでください。

シークレットを公開せずにエラーを処理する

ハンドラーはエラー文字列の照合ではなく、errors.As を使用します。終了状態の TaskFailedError に対しては、タスクIDとエラーコードを含む502レスポンスを返します。その他のラップされたSDKエラーに対しては、復旧用IDを含む502を返します。フォールバックでは、生のエラー、認証情報、プロキシURLを公開せず、固定の400メッセージを返します。

これらの502レスポンスがすべて、解決の失敗を意味するわけではありません。errors.Isでキャンセルと期限切れを区別してください。HTTP固有の処理には、errors.Asを使ってネストされたAPIErrorを取り出し、StatusCodeとRetryAfterを取得できます。すべてのエラーを別のタスクを作成する理由とせず、内部診断ではこの違いを区別してください。

失敗して終了したタスクを再度ポーリングしないでください。エラーテキストが必要な場合は取得し、原因を修正してから新しい解決を開始してください。失敗した解決の料金は返金されます。

リクエストがタイムアウトしてもIDを保持する

キャンセルで停止するのは待機であり、リモート側の処理ではありません。この例では、可能な場合は復旧用のIDを返しますが、永続化はしません。呼び出し元が切断すると、そのJSONレスポンスが届かない可能性があります。

  1. 1.

    ルートを呼び出す前にリクエストIDを割り当てる

    内部の呼び出し元で、論理的に同一の解決ごとに固定のX-Request-IDを保持してください。有効なIDは、ASCIIの英字、数字、アンダースコア、ハイフンからなる1–64文字です。IDがない場合はSDKが生成しますが、事前に割り当てておけば、レスポンスが失われても復旧できます。

  2. 2.

    タスクの入力値をそのまま保持する

    ホスト名、アクション、プロキシ、リクエストIDを、保護されたアプリケーションストレージに保存してください。環境設定の変更後に再試行する際、タスクの入力値を暗黙に別の値へ置き換えてはいけません。

  3. 3.

    新しい処理を作成する前に復旧する

    タスクIDがわかっている場合は、期限を設定した新しいコンテキストでGetTaskを呼び出してください。わからない場合は、同じリクエストIDと入力値を使い、CreateTaskで元のタスクを再送してください。既存のタスクが返されます。同じIDで入力値が異なると競合します。タスクが待機中または解決中の場合に限り、取得を続けてください。

Solveは作成を1回だけ行い、HTTPステータスが429、502、503、504の一時的なポーリングレスポンスのみ再試行します。その際、コンテキストの期限内でRetry-Afterに従います。CreateTaskはPOSTを自動再試行しないため、作成がタイムアウトした場合も、新しいリクエストIDではなく復旧処理が必要です。

返された識別情報とともにトークンを送信する

ハンドラーの成功レスポンスには、認可されたアプリケーションの呼び出し元向けの非公開の解決データが含まれます。対象へのリクエストには、タスクIDではなくSolution.Tokenを使ってください。返されたUserAgentとSecChUAを使い、成功したプロキシはSuccessfulProxy(originalTask, readyResult)で取得してください。

SDKは対象に何も送信しません。タスクが準備完了であることは、解決結果が生成されたことを意味し、ウェブサイトが受け入れたことを意味するわけではありません。対象のレスポンスは別途確認してください。

最初のタスクを実行しましょう。

$5のワークスペースクレジットで始められます。

ワークスペースを作成
GinハンドラーでGo SDKを使用する · solvethree