Llama a la API de CAPTCHA desde Spring Boot

Por solvethree5 min de lectura

Registra un único bean CaptchaClient en el servidor, crea una tarea una sola vez y consulta periódicamente su ID guardado. La clase auxiliar siguiente usa llamadas síncronas de Spring RestClient y devuelve respuestas Gson JsonObject.

En esta página

Añade las dependencias y registra la clase auxiliar

La clase auxiliar proporcionada usa Java 21+, Spring Web 7.0.10 y Gson 2.15.0. En un proyecto Spring Boot existente, conserva la versión compatible de Spring gestionada por Boot en lugar de sobrescribir solo spring-web para que coincida con el ejemplo. Añade Gson como dependencia si tu aplicación aún no lo incluye.

Spring · un servicio RestClient del lado del servidor
// 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.

Guarda la clase como CaptchaClient.java en el paquete de tu aplicación. En un método de fábrica @Bean del servidor, crea una única instancia usando la variable de entorno SOLVETHREE_API_KEY. Comprueba al iniciar que la clave esté configurada. La clase auxiliar la añade como cabecera de autorización Bearer; no la incluyas en el código del navegador.

Reutiliza ese bean mediante inyección por constructor. RestClient se puede reutilizar entre hilos, pero cada solicitud bloquea el hilo que la realiza. No invoques estos métodos en un bucle de eventos de WebFlux. La documentación del cliente REST de Spring describe el cliente síncrono.

El transporte usa un tiempo de espera de conexión de 10 segundos y de lectura de 25 segundos, sin redirecciones. Las respuestas se leen como cadenas y se analizan con Gson, en lugar de asignarse a objetos de respuesta tipados.

Configura el sitio, la acción y el proxy

Antes de llamar a create, configura un perfil activo para el sitio de destino en tu espacio de trabajo. Enviar un nombre de host y una acción no crea ese perfil. Consulta la configuración del perfil del sitio si el sitio aún no está configurado.

  • Pasa el nombre de host como site, no una URL de página completa. Una solicitud de reCAPTCHA no tiene campos url ni type.
  • La clase auxiliar fija action en login. Cambia ese valor o conviértelo en un parámetro del método si la solicitud prevista usa una acción diferente.
  • Pasa una única URL de proxy. La API requiere exactamente uno de los campos proxy o proxyCandidates; esta clase auxiliar implementa la opción de proxy único.
  • La clase auxiliar omite recaptchaType, por lo que la API usa su valor predeterminado enterprise-v3.

Crea una sola vez y consulta periódicamente el ID de tarea guardado

  1. 1.

    Guarda un ID de solicitud antes de crear la tarea

    Elige un requestId que contenga entre 1–64 letras ASCII, números, guiones bajos o guiones. Guárdalo junto con los datos de entrada de la tarea antes de llamar a create. Te permite recuperar la misma tarea si se pierde la respuesta de creación.

  2. 2.

    Llama a create y guarda el id devuelto

    Llama al método create del bean con el nombre de host, el proxy y el ID de solicitud guardado. Envía un único POST a /v1/tasks con waitMs=0. Guarda el id devuelto; usa ese ID de tarea, no el ID de solicitud, para las consultas posteriores.

  3. 3.

    Llama a get hasta que la tarea alcance un estado terminal

    Pasa el ID de tarea guardado a get. Cada consulta solicita waitMs=15000 y bloquea el hilo mientras espera la respuesta. Continúa si el estado es pending o solving, dentro de un plazo límite definido por la aplicación. Detente si es ready o failed. Los tiempos de espera HTTP limitan las solicitudes individuales, no todo el bucle de consulta.

  4. 4.

    Lee la solución solo cuando esté lista

    Para reCAPTCHA, envía solution.token al destino previsto, no el ID de tarea. Usa los valores devueltos de userAgent y secChUa con el mismo proxy con el que se resolvió correctamente. No los sustituyas por cabeceras de navegador ajenas ni cambies la URL de destino. Una tarea lista proporciona una solución; no confirma que el destino haya aceptado tu solicitud posterior.

Gestiona los errores HTTP por separado de las tareas fallidas

La llamada de creación agota el tiempo de espera antes de que recibas un ID de tarea.

Es posible que el servidor haya creado la tarea aunque tu cliente no haya recibido la respuesta.

Prueba esto: Recupera la tarea repitiendo la misma solicitud con el requestId guardado. Si los datos de entrada coinciden, se devuelve la tarea existente. No generes un nuevo ID de solicitud a menos que quieras crear una nueva tarea lógica.

Una llamada a get falla en la capa HTTP.

RestClient.retrieve() lanza una excepción ante respuestas HTTP que no indican éxito. Eso no equivale a un estado de tarea failed.

Prueba esto: Gestiona el error HTTP antes de intentar leer el contenido de una respuesta correcta. Conserva el ID de tarea conocido para recuperarla en lugar de crear otra. Diagnostica los datos de entrada no válidos antes de reintentarlos sin cambios.

La respuesta de la tarea tiene el estado failed.

Lee sus campos errorCode y error; site-unreachable es uno de los posibles códigos de error de tarea.

Prueba esto: Deja de consultar esa tarea y soluciona la causa indicada antes de iniciar otra. Las resoluciones fallidas se reembolsan; no supongas que el fallo significa que no hay saldo suficiente.

Reutilizar un ID de solicitud provoca un conflicto.

Compara los nuevos datos de entrada de la tarea con los guardados para ese ID de solicitud.

Prueba esto: Usa los datos de entrada originales para recuperar la tarea existente. Usa un nuevo ID de solicitud cuando los datos de entrada de la tarea sean distintos de forma intencionada.

Para interpretar los errores, consulta Errores de la API de CAPTCHA.

Ejecuta tu primera tarea.

Empieza con $5 de crédito en tu espacio de trabajo.

Crear un espacio de trabajo
Llama a la API de CAPTCHA desde Spring Boot · solvethree