遍历Axios返回的sources数组发起二次请求并合并多接口结果的实现方案
实现方案
前置注意事项
- 需提前申请HaveIBeenPwned v3接口的API密钥,存入配置文件对应字段
HIBP_API_KEY - HaveIBeenPwned接口要求请求必须携带自定义
User-Agent头,代码中请替换为你的应用名称 - 代码内置单条请求异常兼容,单个source查询失败不会导致整体接口报错,失败项新增字段默认返回空值
- 默认使用并发请求提升响应速度,若触发HIBP限流可改为串行请求(代码后附改造说明)
完整实现代码
// 初始化泄露数据查询接口,对接RapidAPI + HIBP扩展字段 app.get("/lookup/:email/:function", async (req, res) => { try { // 1. 调用RapidAPI获取基础泄露来源数据 const rapidApiOptions = { method: "GET", url: "https://breachdirectory.p.rapidapi.com/", params: { func: req.params.function, term: req.params.email }, headers: { "x-rapidapi-host": "breachdirectory.p.rapidapi.com", "x-rapidapi-key": config.RAPID_API_KEY, }, }; const rapidApiRes = await axios.request(rapidApiOptions); const baseResult = rapidApiRes.data; // 2. 遍历sources批量查询HIBP获取扩展字段 const sourcesWithDetail = await Promise.all( baseResult.sources.map(async (sourceName) => { try { const hibpRes = await axios.get( `https://haveibeenpwned.com/api/v3/breach/${encodeURIComponent(sourceName)}`, { headers: { "hibp-api-key": config.HIBP_API_KEY, "User-Agent": "Your-Application-Name" // 替换为你的应用名称 } } ); return { name: sourceName, title: hibpRes.data.Title, description: hibpRes.data.Description, logoPath: hibpRes.data.LogoPath }; } catch (err) { // 单条请求失败返回默认结构 return { name: sourceName, title: "", description: "", logoPath: "" }; } }) ); // 3. 合并原有字段+扩展后的sources返回 const finalResult = { ...baseResult, sources: sourcesWithDetail }; res.json(finalResult); } catch (globalErr) { // 全局错误处理 console.error(globalErr); res.status(500).json({ error: "查询失败,请稍后重试" }); } });
可选优化:串行请求避免限流
如果并发请求触发HIBP的速率限制,可以把批量查询部分替换为串行遍历:
// 替换原Promise.all部分的代码 const sourcesWithDetail = []; for (const sourceName of baseResult.sources) { try { const hibpRes = await axios.get( `https://haveibeenpwned.com/api/v3/breach/${encodeURIComponent(sourceName)}`, { headers: { "hibp-api-key": config.HIBP_API_KEY, "User-Agent": "Your-Application-Name" } } ); sourcesWithDetail.push({ name: sourceName, title: hibpRes.data.Title, description: hibpRes.data.Description, logoPath: hibpRes.data.LogoPath }); // 可加延迟进一步降低限流风险,比如每次请求间隔1秒 await new Promise(resolve => setTimeout(resolve, 1000)); } catch (err) { sourcesWithDetail.push({ name: sourceName, title: "", description: "", logoPath: "" }); } }
最终返回格式示例
{ "disclaimer": "This data is aggregated from BreachDirectory, HaveIBeenPwned, and Vigilante.pw.", "info": "For full source info, request e.g. https://breachdirectory.tk/api/source?name=Animoto", "sources": [ { "name": "123RF", "title": "123RF", "description": "In March 2020, the stock photo site 123RF suffered a data breach which impacted over 8 million subscribers and was subsequently sold online...", "logoPath": "https://haveibeenpwned.com/Content/Images/PwnedLogos/123RF.png" } // 其余来源项结构同上 ] }
内容的提问来源于stack exchange,提问作者Josh Holly
相关产品推荐
相关产品推荐

