从 Next.js 路由处理程序调用求解器

作者:solvethree阅读需 1 分钟

使用服务端 POST 处理程序创建一次任务,然后通过 GET 按任务 ID 获取任务。将 solvethree 密钥保留在服务器上;浏览器不应直接调用求解服务,也不应在每次等待结束后创建新任务。

本页内容

设置服务器和站点配置

  • 使用 Node.js 22+ 和官方 JavaScript SDK。按照文档中的包 URL 安装方式操作;不要假定 npm 注册表中有可用的包。
  • 在服务器环境中设置 SOLVETHREE_API_KEY、CAPTCHA_SITE、SOLVETHREE_PROXY 和 INTEGRATION_KEY。不要为这两个密钥中的任何一个添加 NEXT_PUBLIC_ 前缀。
  • 调用路由前,先在工作区中激活目标主机名对应的配置。设置 CAPTCHA_SITE 不会创建配置;请参阅站点配置设置。
  • 将示例中的 login 操作替换为集成所需的操作。请求使用一个代理,默认为 reCAPTCHA Enterprise v3;不需要 type 或 url 字段。

添加创建和获取任务的处理程序

将示例放在 app/api/captcha/route.js 中。它导出 App Router HTTP 处理程序,并为 SDK 显式选择 Node.js 运行时。将其保留为在请求时执行的服务端路由,不要设为静态生成的资源。

app/api/captcha/route.js · Node.js 路由处理程序
import { Solvethree, SolvethreeError } from '@solvethree/sdk';
export const runtime = 'nodejs';
const client = new Solvethree({ apiKey: process.env.SOLVETHREE_API_KEY });
function authorized(request) {
  const key = process.env.INTEGRATION_KEY;
  return Boolean(key) && request.headers.get('x-integration-key') === key;
}
function failure(error) {
  return error instanceof SolvethreeError
    ? Response.json({ requestId: error.requestId, taskId: error.taskId }, { status: 502 })
    : Response.json({ error: 'Invalid task input or ID' }, { status: 400 });
}
export async function POST(request) {
  if (!authorized(request)) return new Response('Unauthorized', { status: 401 });
  try {
    return Response.json(await client.createTask({
      site: process.env.CAPTCHA_SITE, action: 'login', proxy: process.env.SOLVETHREE_PROXY,
      requestId: request.headers.get('x-request-id') ?? undefined,
    }, { waitMs: 0, signal: AbortSignal.timeout(20000) }));
  } catch (error) { return failure(error); }
}
export async function GET(request) {
  if (!authorized(request)) return new Response('Unauthorized', { status: 401 });
  const id = new URL(request.url).searchParams.get('id');
  if (!id) return Response.json({ error: 'Provide id' }, { status: 400 });
  try {
    return Response.json(await client.getTask(id, { waitMs: 15000, signal: AbortSignal.timeout(25000) }));
  } catch (error) { return failure(error); }
}

POST 使用 waitMs: 0 调用 createTask,因此会直接返回任务响应,而不等待求解结果。请保存返回的任务 ID。GET 需要此现有 ID,并调用 getTask;它绝不会创建任务。

GET 请求最多等待 15 秒,并另设 25 秒的请求中止时限。POST 的请求中止时限为 20 秒。这些限制针对每个请求,而不是任务的生命周期。

返回任务前逐一检查访问权限

对于浏览器访问,请使用应用实际的服务端身份验证机制,而不是分发 INTEGRATION_KEY。仅验证身份还不够:如果接受已通过身份验证的用户提供的任意任务 ID,他们仍然可以请求他人的结果。

  1. 1.

    将创建的任务与调用者关联

    经过身份验证的 POST 创建任务后,在返回前将其 ID 与已验证的用户或租户关联存储。同时将逻辑 requestId 与该所有者关联,以免恢复时丢失归属记录。

  2. 2.

    获取前检查归属

    处理 GET 时,先验证调用者身份,并根据归属记录检查所提供的 ID,然后再调用 getTask。拒绝不属于调用者的 ID,只有检查通过后才返回结果。

除非应用明确授权调用者选择,否则应由服务器控制站点、操作和代理。此示例中的固定环境值可防止客户端更改这些输入。

轮询同一任务并恢复结果不确定的请求

  1. 1.

    继续轮询尚未进入终态的任务

    对于 pending 或 solving 状态,在应用的总体截止时间内再次获取同一 ID 的任务。GET 等待结束并不是创建新任务的理由。

  2. 2.

    在 ready 或 failed 时停止

    只有 ready 状态包含可用的求解结果。对于 failed,停止轮询并检查 errorCode 和 error;求解失败会退款。在将响应作为任务解析前,先处理非成功 HTTP 响应。路由的 catch 处理程序不能替代对返回任务状态的检查。

  3. 3.

    连接超时后恢复创建请求

    在 POST 前选定并保存 x-request-id。如果已收到任务 ID,则获取该任务。如果创建响应丢失,则使用相同的请求 ID 和未更改的任务输入再次发送 POST,以恢复现有任务。请求 ID 允许包含 1–64 个 ASCII 字母、数字、下划线或连字符;将同一 ID 用于不同输入会产生冲突。

任务处于 ready 状态表示 solvethree 已生成求解结果,并不表示目标已接受该结果。SDK 不会代你提交。提交时,使用返回的 userAgent 和 secChUa,并使用求解成功时所用的代理,也就是此单代理示例中配置的代理。不要切换到无关的浏览器身份或 IP。

遇到请求错误或终态失败时,请先查阅 API 错误参考,再付费进行另一次求解。原样重试无效输入无法解决问题。

运行您的第一个任务。

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

创建工作区
从 Next.js 路由处理程序调用求解器 · solvethree