GinハンドラーでGo SDKを使用する
共有のsolvethreeクライアントを1つ使用し、c.Request.Context() から派生した期限付きの子コンテキストを Solve に渡します。対象設定とAPIキーはサーバー側に保持し、タスクを作成する前にアプリケーションの呼び出し元を認証してください。
このページの内容
依存関係をインストールして対象を設定する
- 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.
サーバーの認証情報を設定する
SOLVETHREE_API_KEYにワークスペースのAPIキーを設定し、INTEGRATION_KEYに内部アプリケーションがX-Integration-Keyで送信するシークレットを設定します。これらは別々の認証情報です。アプリケーションの呼び出し元にsolvethreeのAPIキーは必要ありません。 - 3.
固定の対象を設定する
CAPTCHA_SITEに対象のホスト名を、SOLVETHREE_PROXYに解決に使用するプロキシを設定します。まずワークスペースに有効なサイトプロファイルを作成してください。サイトプロファイルの設定を参照してください。このサンプルではActionをloginに固定しているため、統合で別のアクションを使用する場合は値を変更してください。ホスト名とアクションを設定しても、プロファイルは作成されません。
認証付きGinルートを追加する
クライアントは一度だけ作成され、並行リクエストでも安全に使用できます。ルートはsolvethreeを呼び出す前に、内部アプリケーションの認証情報を確認します。呼び出し元からリクエストIDは受け取りますが、任意の対象やプロキシは受け取りません。
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.
ルートを呼び出す前にリクエストIDを割り当てる
内部の呼び出し元で、論理的に同一の解決ごとに固定の
X-Request-IDを保持してください。有効なIDは、ASCIIの英字、数字、アンダースコア、ハイフンからなる1–64文字です。IDがない場合はSDKが生成しますが、事前に割り当てておけば、レスポンスが失われても復旧できます。 - 2.
タスクの入力値をそのまま保持する
ホスト名、アクション、プロキシ、リクエストIDを、保護されたアプリケーションストレージに保存してください。環境設定の変更後に再試行する際、タスクの入力値を暗黙に別の値へ置き換えてはいけません。
- 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は対象に何も送信しません。タスクが準備完了であることは、解決結果が生成されたことを意味し、ウェブサイトが受け入れたことを意味するわけではありません。対象のレスポンスは別途確認してください。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。