如何使用 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);
请求链路如何工作
用户仍然只需要发起一次请求:
- 用户向 Server 1 的
/payment接口发送 JSON。 - Server 1 使用 Axios 把数据发送给 Server 2。
- Server 1 等待 Server 2 返回结果。
- Server 1 使用
res.send()或res.json()把结果返回给用户。 - 用户端 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 的错误状态码和内容,还要确认响应中不包含内部实现细节、凭据或其他敏感信息。
备注:内容仅供参考。
