在 Gin 处理程序中使用 Go SDK

作者:solvethree阅读需 1 分钟

使用一个共享的 solvethree 客户端,并将派生自 c.Request.Context() 且设置了截止时间的子上下文传给 Solve。将目标配置和 API 密钥保留在服务端,并在创建任务前对应用调用方进行身份验证。

本页内容

安装依赖并配置目标

  1. 1.

    创建 Go 模块并安装依赖

    当前 Gin 版本请使用 Go 1.25 或更高版本。solvethree SDK 本身要求 Go 1.22+。使用 go mod init 和你自己的模块路径初始化应用模块。按照 Gin 快速入门安装 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 响应。兜底处理会返回固定的 400 消息,而不是暴露原始错误、凭据或代理 URL。

这些 502 响应并不都意味着求解失败。使用 errors.Is 区分取消和截止时间到期。对于 HTTP 特定处理,可通过 errors.As 提取嵌套的 APIError,其中包含 StatusCode 和 RetryAfter。应在内部诊断中保留这种区分,而不是将每个错误都视为创建新任务的理由。

已进入失败终态的任务不应再次轮询。如需错误文本,可获取该任务,然后在开始下一次求解之前修正原因。求解失败会退款。

请求超时时保留 ID

取消只会停止等待,不会停止远程工作。示例会在可能时返回恢复所需的 ID,但不会持久化保存它们。如果调用方断开连接,该 JSON 响应可能永远无法送达。

  1. 1.

    调用路由前分配请求 ID

    让内部调用方为每次逻辑求解保留稳定的 X-Request-ID。有效 ID 包含 1–64 个 ASCII 字母、数字、下划线或连字符。缺少 ID 时,SDK 会生成一个,但提前分配 ID 可确保即使响应丢失也能恢复。

  2. 2.

    保留完整且一致的任务输入

    将主机名、操作、代理和请求 ID 存储在受保护的应用存储中。环境配置更改后重试时,不得在未明确告知的情况下替换为不同的任务输入。

  3. 3.

    先恢复,再创建新任务

    如果已知任务 ID,使用新的、有时限的上下文调用 GetTask。如果没有任务 ID,则使用相同的请求 ID 和输入,通过 CreateTask 重新提交原始任务。这会返回现有任务;相同 ID 搭配不同输入会产生冲突。仅在任务处于等待或求解中状态时继续获取。

Solve 只创建一次,并且仅重试 HTTP 状态为 429、502、503 或 504 的临时轮询响应,在上下文截止时间内遵循 Retry-After。CreateTask 从不自动重试 POST,因此创建超时后仍需恢复,而不是使用新的请求 ID。

使用返回的身份信息提交令牌

处理函数成功响应中包含供已授权应用调用方使用的私密求解数据。在目标请求中使用 Solution.Token,而不是任务 ID。使用返回的 UserAgent 和 SecChUA,并通过 SuccessfulProxy(originalTask, readyResult) 获取成功求解所用的代理。

SDK 不会向目标提交任何内容。任务就绪表示已生成求解结果,并不表示网站已接受它。请单独检查目标的响应。

运行您的第一个任务。

使用 $5 工作区额度开始体验。

创建工作区
在 Gin 处理程序中使用 Go SDK · solvethree