You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在响应GET请求前等待第三方服务器的POST回调

问题

我有一台用于封装API并处理前端额外需求的服务器。每当服务器收到请求时,会先完成自身处理,再向一台不受我控制的第三方服务器发送POST请求。该请求处理耗时较长,第三方服务器支持通过传入callback_url,在处理完成后向该地址发送POST回调。

当前代码中,GET接口/my_url发起请求后会立即返回“处理已启动”的响应,但我需要让该GET接口等待/callback_here收到第三方的POST回调后,完成后续处理再向前端返回最终响应。请问该如何实现?有没有更优的方案?

现有代码示例

app.get(`/my_url`, async (c) => {
  // 执行一些前置处理

  const formData = new FormData()
  // 添加其他所需参数
  formData.append(`callback_url`, `https://my-url.com/callback_here`)

  const response = await fetch(`external-url.com`, {
    method: `POST`,
    body: formData,
  }).then(response => response.json())

  // 现在会立即返回“处理已启动”的响应
})

app.post(`/callback_here`, async (c) =>{
  // 第三方处理完成后会向这里发送POST请求
}) 

解决方案

方案一:基于内存的请求等待(适合小流量场景)

核心思路是用全局Map存储每个请求的Promise resolver,让/my_url接口等待对应回调触发后再返回响应。

实现代码

// 全局存储请求的回调 resolver,key用唯一请求ID
const pendingRequests = new Map()

app.get(`/my_url`, async (c) => {
  // 生成唯一请求ID,这里用Node.js内置的crypto模块生成UUID
  const requestId = crypto.randomUUID()

  // 创建Promise,将resolver存入全局Map
  const waitForCallback = new Promise((resolve) => {
    pendingRequests.set(requestId, resolve)
  })

  const formData = new FormData()
  // 把requestId附加到callback_url上,让第三方回调时带回
  formData.append(`callback_url`, `https://my-url.com/callback_here?requestId=${requestId}`)

  // 发送请求给第三方
  await fetch(`external-url.com`, {
    method: `POST`,
    body: formData,
  })

  // 等待回调触发
  const callbackData = await waitForCallback
  // 执行后续处理逻辑
  const finalResult = handleCallbackData(callbackData)

  // 返回最终响应给前端
  return c.json(finalResult)
})

app.post(`/callback_here`, async (c) =>{
  // 从请求参数中获取第三方传回的requestId
  const { requestId } = c.request.query
  const callbackData = await c.request.json()

  // 取出对应的resolver
  const resolve = pendingRequests.get(requestId)
  if (resolve) {
    // 触发Promise,让等待的GET接口继续执行
    resolve(callbackData)
    // 清理Map中的数据,避免内存泄漏
    pendingRequests.delete(requestId)
  }

  // 给第三方返回成功响应
  return c.json({ status: 'success' })
})

// 自定义的后续处理函数
function handleCallbackData(data) {
  // 这里编写回调数据的处理逻辑
  return { processed: true, data }
}

优缺点

  • 优点:实现简单,无需额外依赖。
  • 缺点:
    • 内存存储,服务器重启后未处理的请求会丢失;
    • 不适合大流量场景,Map会占用过多内存;
    • 需额外添加超时处理,避免请求无限挂起,可通过Promise.race结合setTimeout实现。

方案二:持久化队列方案(生产环境推荐)

如果是生产环境或大流量场景,建议用持久化消息队列(如Redis)实现,避免内存泄漏和服务器重启丢失请求的问题。

实现思路

  1. /my_url生成唯一requestId,发送请求时将ID传递给第三方;
  2. 用Redis的BLPOP(阻塞式弹出)让接口等待对应ID的消息;
  3. /callback_here收到回调后,将数据存入Redis对应队列;
  4. BLPOP获取到数据后,完成后续处理并返回响应。

示例代码(基于Redis)

import redis from 'redis'

// 初始化Redis客户端
const client = redis.createClient()
await client.connect()

app.get(`/my_url`, async (c) => {
  const requestId = crypto.randomUUID()

  const formData = new FormData()
  formData.append(`callback_url`, `https://my-url.com/callback_here?requestId=${requestId}`)

  await fetch(`external-url.com`, {
    method: `POST`,
    body: formData,
  })

  // 阻塞等待Redis队列消息,超时时间设为5分钟(可按需调整)
  const [, callbackDataStr] = await client.blPop(`callback:${requestId}`, 300)
  if (!callbackDataStr) {
    return c.json({ error: '请求超时' }, { status: 408 })
  }

  const callbackData = JSON.parse(callbackDataStr)
  const finalResult = handleCallbackData(callbackData)

  return c.json(finalResult)
})

app.post(`/callback_here`, async (c) =>{
  const { requestId } = c.request.query
  const callbackData = await c.request.json()

  // 将回调数据存入Redis队列
  await client.rPush(`callback:${requestId}`, JSON.stringify(callbackData))
  // 设置队列过期时间,避免Redis内存浪费
  await client.expire(`callback:${requestId}`, 3600)

  return c.json({ status: 'success' })
})

优缺点

  • 优点:
    • 持久化存储,服务器重启后未处理请求不会丢失;
    • 适合大流量场景,Redis性能足够支撑;
    • 可灵活设置超时时间,避免请求无限等待。
  • 缺点:需额外部署Redis服务,增加架构复杂度。

额外注意事项

  • 必须给GET接口设置超时时间,避免前端和服务器长时间挂起连接;
  • 第三方回调可能重复触发,需做幂等处理;
  • 如果第三方不支持传递自定义参数,可将requestId嵌入callback_url路径,如https://my-url.com/callback_here/${requestId};
  • 生产环境要做好日志监控,记录每个请求的requestId、回调状态等信息,方便排查问题。

内容的提问来源于stack exchange,提问作者cSharp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:43:17