
本文详解在 Express 中调用异步 fetch 函数时常见的“返回函数而非结果”错误,说明为何 res.json(getPhones) 不生效,并提供 async/await 与 Promise 链两种规范写法,确保 XML 解析后的结构化数据能正确序列化为 JSON 响应。
本文详解在 express 中调用异步 `fetch` 函数时常见的“返回函数而非结果”错误,说明为何 `res.json(getphones)` 不生效,并提供 `async/await` 与 promise 链两种规范写法,确保 xml 解析后的结构化数据能正确序列化为 json 响应。
在构建 Express API 时,若后端需通过 fetch 调用外部 SOAP/XML 接口(如 Cisco UCM 的 AXL API),常会遇到「控制台能打印数据,但 API 响应为空」的问题。根本原因在于:你导出了函数本身,而非函数执行后返回的 Promise 结果。
例如,原始代码中:
export const phonesData = (req, res) => {
res.status(200).json(getPhones) // ❌ 错误:传入的是函数引用,不是数据
}getPhones 是一个 async 函数,其类型为 () => Promise<array>></array>,直接传给 res.json() 会导致 Express 将该函数对象序列化(通常为 {} 或空响应),而非等待并发送解析后的数组。
✅ 正确做法是 调用函数并等待其 Promise 解析完成。推荐使用 async/await 写法,语义清晰、错误处理统一:
export const phonesData = async (req, res) => {
try {
const data = await getPhones(); // ✅ 调用并等待解析结果
res.status(200).json(data);
} catch (error) {
console.error('Failed to fetch phones:', error);
res.status(500).json({ error: 'Failed to retrieve phone data' });
}
};同时,务必重构 getPhones 函数,避免混用 async/await 和 .then() —— 这不仅降低可读性,还易导致未捕获的 Promise 拒绝(如 fetch 失败但 .catch() 未覆盖所有链路):
const getPhones = async () => {
try {
const response = await fetch(url, requestOptions);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const XMLdata = await response.text();
const parser = new XMLParser();
const jObj = parser.parse(XMLdata);
// 安全取值(建议增加空值检查)
const phones = jObj?.['soapenv:Envelope']?.['soapenv:Body']?.['ns:listPhoneResponse']?.return?.phone;
if (!Array.isArray(phones)) {
throw new Error('Unexpected XML structure: "phone" array not found');
}
console.log('Parsed phones:', phones);
return phones;
} catch (error) {
console.error('Error in getPhones:', error);
throw error; // 重新抛出,使上层 catch 可捕获
}
};⚠️ 注意事项:
-
不要省略
await:getPhones()必须加括号调用,且因返回 Promise,必须await(或.then())才能获取实际数据; -
错误要透出:
getPhones中的catch不应仅console.log,而应throw error,确保 Express 路由处理器能统一处理异常; -
XML 路径健壮性:SOAP 响应结构易变,建议用可选链(
?.)+ 类型校验,避免运行时Cannot read property 'phone' of undefined; -
避免混合语法:同一函数内不要既用
await又用.then(),保持风格一致,降低维护成本。
最后,在路由中注册时确保使用更新后的处理器:
// routes/phones.js
import { phonesData } from '../controllers/phoneController.js';
router.get('/api/phones', phonesData); // ✅ 已支持 async通过以上修正,访问 http://localhost:3000/api/phones 将稳定返回如下 JSON 响应:
[
{"name":"SEP805E0C47A189","model":"Third-party SIP Device (Basic)"},
{"name":"SEPDCEB94CE9A65","model":"Cisco 7821"},
{"name":"TCT-442001","model":"Cisco Dual Mode for iPhone"},
{"name":"BOT-441015","model":"Cisco Dual Mode for Android"},
{"name":"SEP70708B219FC8","model":"Cisco 8851"}
]这不仅是语法修复,更是异步编程思维的实践:函数定义 ≠ 函数执行,Promise 需显式消费,错误需主动传播。

















