PAYATHON 2026

如何使用 axios 将两次请求的最终响应返回给用户

支付阿杰

结论

Server 1 需要等待 Server 2 完成请求,再通过 Express 的 res.status(...).send(...) 或 res.json(...) 把结果返回给用户。

现有代码中的 res.send(response.data) 已经可以返回 Server 2 的成功响应。问题出在错误和超时处理上:当前的 catch 只记录错误,没有向用户发送任何响应。用户会一直等待,直到客户端自身超时。

推荐写法

使用 async/await 后,请求流程会更容易理解:

app.post('/payment', async (req, res) => {
  const data = {
    ...req.body,
    CreditboxToken: creditToken
  };

  try {
    const response = await axios({
      method: 'post',
      url: urlPayment,
      headers: {
        'Content-Type': 'application/json'
      },
      timeout: 30000,
      data
    });

    // 将 Server 2 的状态码和响应内容转发给用户
    return res.status(response.status).send(response.data);
  } catch (error) {
    // Server 2 已返回非 2xx 响应
    if (error.response) {
      return res
        .status(error.response.status)
        .send(error.response.data);
    }

    // Server 1 请求 Server 2 时超时
    if (error.code === 'ECONNABORTED') {
      return res.status(504).json({
        message: 'Server 2 response timeout'
      });
    }

    // 网络错误、连接失败等其他情况
    console.error(error);

    return res.status(502).json({
      message: 'Unable to communicate with Server 2'
    });
  }
});

如果可以确定 Server 2 始终返回 JSON,成功响应也可以写成:

return res.status(response.status).json(response.data);

请求链路如何工作

用户仍然只需要发起一次请求:

  1. 用户向 Server 1 的 /payment 接口发送 JSON。
  2. Server 1 使用 Axios 把数据发送给 Server 2。
  3. Server 1 等待 Server 2 返回结果。
  4. Server 1 使用 res.send() 或 res.json() 把结果返回给用户。
  5. 用户端 Axios 的 then 或 catch 收到最终响应。

用户端可以这样处理:

axios({
  method: 'post',
  url: Url,
  headers: {
    'Content-Type': 'application/json'
  },
  timeout: 35000,
  data
})
  .then(function (response) {
    console.log(response.status);
    console.log(response.data);
  })
  .catch(function (error) {
    if (error.response) {
      console.log(error.response.status);
      console.log(error.response.data);
    } else if (error.code === 'ECONNABORTED') {
      console.log('Request to Server 1 timed out');
    } else {
      console.log(error.message);
    }
  });

这里把用户端的超时时间设为 35000 毫秒,给 Server 1 留出处理请求和转发响应的时间。如果用户端和 Server 1 都恰好设置为 30000 毫秒,用户端可能先超时,因而收不到 Server 1 返回的 504 响应。具体时间应根据实际业务调整。

保留 Promise 写法也可以

原来的结构不必彻底改写,只需保证成功和失败分支都会向用户返回响应:

app.post('/payment', (req, res) => {
  const data = {
    ...req.body,
    CreditboxToken: creditToken
  };

  axios({
    method: 'post',
    url: urlPayment,
    headers: {
      'Content-Type': 'application/json'
    },
    timeout: 30000,
    data
  })
    .then(function (response) {
      return res.status(response.status).send(response.data);
    })
    .catch(function (error) {
      if (error.response) {
        return res
          .status(error.response.status)
          .send(error.response.data);
      }

      if (error.code === 'ECONNABORTED') {
        return res.status(504).json({
          message: 'Server 2 response timeout'
        });
      }

      console.error(error);

      return res.status(502).json({
        message: 'Unable to communicate with Server 2'
      });
    });
});

注意事项

不要提前发送 200

一次 HTTP 请求只能发送一次最终响应。如果 Server 1 先向用户返回 200,之后就无法通过同一个请求继续发送 Server 2 的最终结果。

如果业务需要立即确认“任务已接收”,然后异步返回处理结果,就要改用任务 ID 配合轮询、WebSocket、Server-Sent Events 或回调接口,不能继续等待同一个 HTTP 响应。

使用明确的路由方法

这里处理的是 POST 请求,建议使用:

app.post('/payment', handler);

app.use('/payment', handler) 会匹配多种 HTTP 方法,以及该路径下的其他子路径,范围通常太大。

确保能够解析 JSON

使用较新的 Express 写法时,应在路由前注册 JSON 解析中间件:

app.use(express.json());

否则 req.body 可能是 undefined。

不要直接修改 req.body

建议创建一个新对象:

const data = {
  ...req.body,
  CreditboxToken: creditToken
};

这样不会修改 Express 提供的原始请求对象。

区分不同失败类型

Axios 常见的错误情况包括:

  • error.response:Server 2 已经响应,但状态码不在 Axios 默认接受的 2xx 范围内。
  • error.request:请求已经发出,但没有收到响应。
  • error.code === 'ECONNABORTED':通常表示 Axios 请求超时。
  • 其他错误:可能来自配置、DNS、连接或程序内部。

Server 1 通常可以用 504 Gateway Timeout 表示上游超时,用 502 Bad Gateway 表示无法从上游服务获得有效响应。如果需要原样转发 Server 2 的错误状态码和内容,还要确认响应中不包含内部实现细节、凭据或其他敏感信息。

备注:内容仅供参考。