从 Spring Boot 调用 CAPTCHA API

作者:solvethree阅读需 1 分钟

注册一个服务端 CaptchaClient bean,仅创建一次任务,然后使用已保存的 ID 轮询。下面的辅助类使用同步的 Spring RestClient 调用,并返回 Gson JsonObject 响应。

本页内容

添加依赖并注册辅助类

提供的辅助类使用 Java 21+、Spring Web 7.0.10 和 Gson 2.15.0。在现有 Spring Boot 项目中,请保留由 Boot 管理的兼容 Spring 版本,不要为了匹配示例而仅覆盖 spring-web 的版本。如果应用尚未包含 Gson,请将其添加为依赖。

Spring · 服务端 RestClient 服务
// Java 21+, Spring Web 7.0.10 and Gson 2.15.0; use your Boot dependency management.
// Save as CaptchaClient.java in your application's package.
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.http.HttpClient;
import java.time.Duration;
import org.springframework.http.MediaType;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.web.client.RestClient;

public final class CaptchaClient {
    private final RestClient api;
    public CaptchaClient(String key) {
        var transport = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10))
            .followRedirects(HttpClient.Redirect.NEVER).build();
        var factory = new JdkClientHttpRequestFactory(transport);
        factory.setReadTimeout(Duration.ofSeconds(25));
        api = RestClient.builder().baseUrl("https://api.solvethree.com/v1")
            .requestFactory(factory).defaultHeader("Authorization", "Bearer " + key).build();
    }
    public JsonObject create(String site, String proxy, String requestId) {
        var input = new JsonObject();
        input.addProperty("site", site);
        input.addProperty("action", "login");
        input.addProperty("proxy", proxy);
        input.addProperty("requestId", requestId); // Save before calling create.
        String body = api.post().uri("/tasks?waitMs=0").contentType(MediaType.APPLICATION_JSON)
            .body(input.toString()).retrieve().body(String.class);
        return JsonParser.parseString(body).getAsJsonObject();
    }
    public JsonObject get(String id) {
        if (!id.matches("[A-Za-z0-9_-]+")) throw new IllegalArgumentException("Invalid task ID");
        String body = api.get().uri("/tasks/{id}?waitMs=15000", id)
            .retrieve().body(String.class);
        return JsonParser.parseString(body).getAsJsonObject();
    }
}
// Construct once with System.getenv("SOLVETHREE_API_KEY") in a server-side @Bean.
// Call create once, save its id, then get(id) until ready/failed or your deadline.
// RestClient throws on HTTP errors; keep the saved requestId and any known task id.

将该类保存为应用包中的 CaptchaClient.java。在服务端的 @Bean 工厂方法中,使用 SOLVETHREE_API_KEY 环境变量构造一次实例。启动时检查是否已配置密钥。辅助类会将其添加到 Bearer 授权标头中;不要将其放入浏览器端代码。

通过构造函数注入复用该 bean。RestClient 可以跨线程复用,但每次请求都会阻塞调用线程。不要在 WebFlux 事件循环中调用这些方法。Spring 的 REST 客户端文档介绍了该同步客户端。

传输层使用 10 秒连接超时和 25 秒读取超时,且不跟随重定向。响应以字符串形式读取并由 Gson 解析,不会映射为强类型响应对象。

设置站点、操作和代理

调用 create 前,请在工作区中为目标站点配置一个已启用的配置文件。发送主机名和操作不会创建该配置文件。如果尚未配置站点,请参阅站点配置文件设置。

  • 将主机名作为 site 传入,而不是完整的页面 URL。reCAPTCHA 请求没有 url 或 type 字段。
  • 辅助类将 action 硬编码为 login。如果预期请求使用不同的操作,请修改该值,或将其设为方法参数。
  • 传入一个代理 URL。API 要求在 proxy 和 proxyCandidates 中且仅指定其中一个;此辅助类实现的是单代理选项。
  • 辅助类省略了 recaptchaType,因此 API 使用默认值 enterprise-v3。

仅创建一次,然后使用已保存的任务 ID 轮询

  1. 1.

    创建前保存请求 ID

    选择一个包含 1–64 个 ASCII 字母、数字、下划线或连字符的 requestId。调用 create 前,将其与任务输入一起保存。如果创建响应丢失,可用它恢复同一个任务。

  2. 2.

    调用 create 并保存返回的 id

    使用主机名、代理和已保存的请求 ID 调用 bean 的 create 方法。该方法向 /v1/tasks 发送一次 POST 请求,并设置 waitMs=0。保存返回的 id;后续查询使用该任务 ID,而不是请求 ID。

  3. 3.

    调用 get,直到任务达到终态

    将已保存的任务 ID 传给 get。每次查询都会请求 waitMs=15000,并在等待响应时阻塞。对于 pending 或 solving,在应用层截止时间内继续轮询。对于 ready 或 failed,停止轮询。HTTP 超时限制的是单次请求,而不是整个轮询循环。

  4. 4.

    仅在就绪时读取求解结果

    对于 reCAPTCHA,将 solution.token 提交给预期目标,而不是提交任务 ID。使用返回的 userAgent 和 secChUa,并继续使用成功求解时的同一代理。不要替换为无关的浏览器标头,也不要更改目标 URL。就绪的任务会提供求解结果,但这并不代表目标已接受后续请求。

分别处理 HTTP 错误和任务失败

create 调用在收到任务 ID 前超时。

即使客户端未收到响应,服务端也可能已创建任务。

试试这样做: 使用已保存的 requestId 重复发送相同的任务请求来恢复。输入匹配时会返回现有任务。除非打算创建新的逻辑任务,否则不要生成新的请求 ID。

get 调用在 HTTP 层失败。

RestClient.retrieve() 会在收到非成功 HTTP 响应时抛出异常。这不等同于任务状态为 failed。

试试这样做: 在尝试读取成功响应体前,先处理 HTTP 错误。保留已知任务 ID 以便恢复,而不是创建另一个任务。对于无效输入,先查明问题,再决定是否原样重试。

任务响应的状态为 failed。

读取其 errorCode 和 error;site-unreachable 是可能出现的任务错误码之一。

试试这样做: 停止轮询该任务,并在开始另一个任务前解决所报告的原因。求解失败会退款;不要认为失败就意味着余额不足。

复用请求 ID 时发生冲突。

将新的任务输入与该请求 ID 对应的已保存输入进行比较。

试试这样做: 使用原始输入恢复现有任务。如果有意使用不同的任务输入,请使用新的请求 ID。

有关错误解释,请参阅 CAPTCHA API 错误。

运行您的第一个任务。

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

创建工作区
从 Spring Boot 调用 CAPTCHA API · solvethree