Spring BootからCAPTCHA APIを呼び出す
サーバー側で CaptchaClient Beanを1つ登録し、タスクを一度だけ作成して、保存したIDでポーリングします。以下のヘルパーはSpringの同期 RestClient 呼び出しを使用し、Gsonの JsonObject レスポンスを返します。
このページの内容
依存関係を追加してヘルパーを登録する
付属のヘルパーはJava 21+、Spring Web 7.0.10、Gson 2.15.0を使用します。既存のSpring Bootプロジェクトでは、サンプルに合わせて spring-web だけを上書きせず、Bootが管理する互換性のあるSpringバージョンを維持してください。アプリケーションにGsonがまだ含まれていない場合は、依存関係として追加してください。
// 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を1つ渡してください。APIでは
proxyまたはproxyCandidatesのいずれか一方だけが必要です。このヘルパーは単一プロキシの方式を実装しています。 - このヘルパーは
recaptchaTypeを省略するため、APIはデフォルトのenterprise-v3を使用します。
一度だけ作成し、保存したタスクIDでポーリングする
- 1.
作成前にリクエストIDを保存する
ASCIIの英字、数字、アンダースコア、ハイフンからなる1–64文字の
requestIdを選びます。createを呼び出す前に、タスクの入力値とともに保存してください。作成レスポンスが失われた場合でも、同じタスクを復旧できます。 - 2.
createを呼び出し、返されたidを保存する
Beanの
createメソッドにホスト名、プロキシ、保存したリクエストIDを渡して呼び出します。waitMs=0を指定し、/v1/tasksにPOSTを1回送信します。返されたidを保存してください。以降の取得には、リクエストIDではなく、このタスクIDを使用します。 - 3.
タスクが終了ステータスになるまでgetを呼び出す
保存したタスクIDを
getに渡します。各取得リクエストではwaitMs=15000を指定し、レスポンスを待つ間はブロックします。pendingまたはsolvingの場合は、アプリケーションレベルの期限内で続行します。readyまたはfailedで停止します。HTTPタイムアウトが制限するのは個々のリクエストであり、ポーリングループ全体ではありません。 - 4.
準備完了時にのみソリューションを読み取る
reCAPTCHAでは、タスクIDではなく
solution.tokenを本来の送信先に送信します。返されたuserAgentとsecChUaを、解決に成功したときと同じプロキシで使用してください。無関係なブラウザーヘッダーに置き換えたり、送信先URLを変更したりしないでください。準備完了のタスクはソリューションを提供しますが、その後のリクエストを送信先が受け入れたことを保証するものではありません。
HTTPエラーとタスクの失敗を分けて処理する
- タスクIDを受け取る前にcreateの呼び出しがタイムアウトする。
クライアントがレスポンスを受信していなくても、サーバー側ではタスクが作成されている可能性があります。
試してみましょう: 保存した
requestIdを使い、同じタスクリクエストを再送して復旧します。入力値が一致すれば、既存のタスクが返されます。新しい論理タスクを作成する意図がない限り、新しいリクエストIDを生成しないでください。- getの呼び出しがHTTP層で失敗する。
RestClient.retrieve()は、成功以外のHTTPレスポンスで例外をスローします。これはタスクのステータスがfailedであることとは異なります。試してみましょう: 成功時のペイロードを読み取ろうとする前に、HTTPエラーを処理してください。別のタスクを作成せず、復旧に使えるよう既知のタスクIDを保持します。無効な入力値は、そのまま再試行する前に原因を調べてください。
- タスクのレスポンスのステータスが
failedになっている。 その
errorCodeとerrorを確認してください。タスクのエラーコードにはsite-unreachableなどがあります。試してみましょう: そのタスクのポーリングを停止し、別のタスクを開始する前に、報告された原因に対処してください。解決に失敗した場合は返金されます。失敗がクレジット不足を意味するとは限りません。
- リクエストIDを再利用すると競合が発生する。
新しいタスクの入力値を、そのリクエストIDに対して保存した入力値と比較してください。
試してみましょう: 既存のタスクを復旧するには、元の入力値を使用します。意図的に異なるタスク入力値を使う場合は、新しいリクエストIDを使用してください。
エラーの意味については、CAPTCHA APIエラーを参照してください。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。