1508 字
8 分钟
修复Umami统计数据显示问题 - 适配新版API响应格式
修复Umami统计数据显示问题 - 适配新版API响应格式
从问题诊断到完美解决,记录一次API格式变更的修复之旅
🐛 问题现象
在使用Cloudflare Worker代理Umami统计API时,突然发现网站的浏览量和访客数一直显示为0:
{ "total": 0, "visitors": 0, "visits": 0, "bounces": 0, "totaltime": 0, "cached": false, "timestamp": 1763208601158}这显然不正常,因为网站实际上有大量的访问数据。
🔍 问题诊断
初步排查
首先检查了几个常见问题:
- ✅ API Token配置正确
- ✅ 网站ID正确
- ✅ Worker部署成功
- ✅ CORS配置正常
既然基础配置都没问题,那问题很可能出在数据解析上。
深入分析
通过添加调试代码,我发现Umami API确实返回了数据,但Worker脚本无法正确提取。查看Umami官方文档后发现:
Umami API响应格式已经更新!
📊 API格式变化对比
旧版格式(嵌套对象)
{ "pageviews": { "value": 34210, "change": 10 }, "visitors": { "value": 9401, "change": 7 }, "visits": { "value": 12726, "change": 8 }}新版格式(扁平化)
{ "pageviews": 34210, "visitors": 9401, "visits": 12726, "bounces": 7918, "totaltime": 5214477, "comparison": { "pageviews": 30818, "visitors": 8776, "visits": 11756, "bounces": 7396, "totaltime": 1704355 }}关键变化:
- ❌ 旧版:数据包装在
value字段中 - ✅ 新版:数据直接作为顶层字段
- ➕ 新增:
comparison对比数据
🔧 修复方案
问题代码
旧代码尝试从 value 字段提取数据:
const result = { total: data.pageviews?.value || 0, // ❌ 新版API中不存在value字段 visitors: data.visitors?.value || 0, visits: data.visits?.value || 0, bounces: data.bounces?.value || 0, totaltime: data.totaltime?.value || 0,};修复后代码
直接从顶层字段获取数据:
const result = { total: data.pageviews || 0, // ✅ 直接获取数值 visitors: data.visitors || 0, visits: data.visits || 0, bounces: data.bounces || 0, totaltime: data.totaltime || 0, cached: false, timestamp: Date.now(),};📝 完整修复步骤
1. 修复总浏览量API
修改 getTotalPageviews 函数:
async function getTotalPageviews(request) { const cacheUrl = new URL(request.url); cacheUrl.pathname = "/cache/total"; const cacheKey = new Request(cacheUrl);
// 尝试从缓存获取 const cached = await getCache(cacheKey); if (cached) { return cached; }
// 计算时间范围 (所有时间) const endDate = new Date(); const startDate = new Date("2020-01-01"); // 获取全部历史数据
const startAt = startDate.getTime(); const endAt = endDate.getTime();
// 调用 Umami API const apiUrl = `${CONFIG.UMAMI_API_URL}/websites/${CONFIG.UMAMI_WEBSITE_ID}/stats?startAt=${startAt}&endAt=${endAt}`;
const response = await fetch(apiUrl, { headers: { Authorization: `Bearer ${CONFIG.UMAMI_API_TOKEN}`, "Content-Type": "application/json", }, });
if (!response.ok) { const errorText = await response.text(); throw new Error(`Umami API error: ${response.status} - ${errorText}`); }
const data = await response.json();
// ✅ 直接从顶层字段获取数据 const result = { total: data.pageviews || 0, visitors: data.visitors || 0, visits: data.visits || 0, bounces: data.bounces || 0, totaltime: data.totaltime || 0, cached: false, timestamp: Date.now(), };
// 缓存结果 await setCache(cacheKey, result, CONFIG.CACHE_TTL);
return jsonResponse(result, 200, request);}2. 修复页面浏览量API
修改 getPagePageviews 函数:
async function getPagePageviews(request, pageUrl) { const cacheUrl = new URL(request.url); cacheUrl.pathname = `/cache/page${pageUrl.startsWith("/") ? pageUrl : "/" + pageUrl}`; const cacheKey = new Request(cacheUrl);
// 尝试从缓存获取 const cached = await getCache(cacheKey); if (cached) { return cached; }
// 计算时间范围 (所有时间) const endDate = new Date(); const startDate = new Date("2020-01-01"); const startAt = startDate.getTime(); const endAt = endDate.getTime();
// 使用 /stats API 并通过 url 参数过滤 const apiUrl = `${CONFIG.UMAMI_API_URL}/websites/${CONFIG.UMAMI_WEBSITE_ID}/stats?startAt=${startAt}&endAt=${endAt}&url=${encodeURIComponent(pageUrl)}`;
const response = await fetch(apiUrl, { headers: { Authorization: `Bearer ${CONFIG.UMAMI_API_TOKEN}`, "Content-Type": "application/json", }, });
if (!response.ok) { const errorText = await response.text(); throw new Error(`Umami API error: ${response.status} - ${errorText}`); }
const data = await response.json();
// ✅ 直接从顶层字段获取数据 const result = { url: pageUrl, pageviews: data.pageviews || 0, visitors: data.visitors || 0, cached: false, timestamp: Date.now(), };
// 缓存结果 await setCache(cacheKey, result, CONFIG.CACHE_TTL);
return jsonResponse(result, 200, request);}3. 优化缓存机制
将简单的内存缓存升级为Cloudflare Cache API:
/** * 使用 Cloudflare Cache API 进行缓存 */async function getCache(cacheKey) { const cache = caches.default; const response = await cache.match(cacheKey); if (!response) { return null; }
// 检查缓存是否过期 const expiry = response.headers.get("Cache-Expiry"); if (expiry && Date.now() > parseInt(expiry, 10)) { caches.default.delete(cacheKey); return null; } return response;}
async function setCache(cacheKey, data, ttlSeconds) { const cache = caches.default; const responseBody = JSON.stringify(data); const headers = { "Content-Type": "application/json", "Cache-Control": `public, max-age=${ttlSeconds}`, "Cache-Expiry": (Date.now() + ttlSeconds * 1000).toString(), }; const response = new Response(responseBody, { headers }); await cache.put(cacheKey, response);}✅ 修复效果
修复后,API返回正常数据:
{ "total": 34210, "visitors": 9401, "visits": 12726, "bounces": 7918, "totaltime": 5214477, "cached": false, "timestamp": 1763209211581}网站上的统计数据也正常显示了!
🎯 关键改进点
1. API格式适配
- ✅ 移除了对
.value字段的依赖 - ✅ 直接从顶层获取数据
- ✅ 保持向后兼容性
2. 时间范围优化
- ✅ 从”最近30天”改为”所有时间”
- ✅ 显示完整的历史统计数据
3. 缓存升级
- ✅ 从内存Map升级到Cache API
- ✅ 更持久、更高效
- ✅ 支持边缘缓存
4. 错误处理增强
- ✅ 添加详细的错误信息
- ✅ 包含HTTP状态码和响应文本
- ✅ 便于问题诊断
💡 经验总结
遇到API数据异常时的排查思路
-
确认基础配置
- API Token是否正确
- 端点URL是否正确
- 权限是否充足
-
检查API响应
- 添加调试日志
- 查看原始响应数据
- 对比API文档
-
验证数据解析
- 检查字段访问路径
- 确认数据类型
- 处理边界情况
-
测试修复效果
- 清除缓存
- 重新部署
- 验证各个端点
最佳实践建议
-
版本兼容性
// 同时支持新旧格式const value = data.field?.value || data.field || 0; -
错误处理
if (!response.ok) {const errorText = await response.text();throw new Error(`API error: ${response.status} - ${errorText}`);} -
缓存策略
// 使用合适的TTLconst CACHE_TTL = 300; // 5分钟 -
调试信息
// 开发环境添加debug字段if (isDev) {result.debug = { apiUrl, rawData: data };}
🔗 相关资源
📌 总结
这次修复让我深刻体会到:
- API文档很重要 - 定期查看官方文档,了解API变更
- 调试信息必不可少 - 添加适当的日志可以快速定位问题
- 错误处理要完善 - 详细的错误信息能节省大量排查时间
- 缓存要谨慎使用 - 确保缓存不会掩盖真实问题
希望这篇文章能帮助遇到类似问题的开发者快速解决问题!
创建日期: 2025年11月15日
最后更新: 2025年11月15日
版本: 1.0.0
状态: ✅ 已完成
修复Umami统计数据显示问题 - 适配新版API响应格式
https://www.freebird2913.tech/posts/umami-api-fix-cloudflare-worker/