关于 har-replay
面向 Lightdash 前端开发的 HAR 重放技能,可将 HAR 文件转换为临时模拟后端,让页面使用捕获的生产数据渲染,无需数据库、数仓或身份验证。
主要功能
- 01分析 HAR 请求与页面信息
- 02按请求路径及请求体匹配
- 03解码响应并处理状态码
- 04生成临时 Node.js 重放服务
- 05运行前端并验证页面
适用场景
- 01用捕获的数据复现前端页面
- 02在无数据库环境下检查仪表板
- 03排查 HAR 缺失请求或响应格式问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lightdash/ lightdash --skill har-replay安装后在 Claude Code 中输入/har-replay确认已加载。
详细介绍
HAR 回放
将 HAR 文件回放为模拟的 Lightdash 后端,让前端使用完全一致的生产数据进行渲染。无需数据库、数据仓库或身份验证。
用户必须提供 .har 文件的路径。如果未通过 $ARGUMENTS 提供,请询问用户。
步骤 1:分析 HAR 文件
运行 Python 脚本,从 HAR 中提取关键信息:
import json, sys, re
from collections import Counter
from urllib.parse import urlparse
har_path = "$HAR_FILE_PATH"
with open(har_path) as f:
har = json.load(f)
entries = har['log']['entries']
# Extract the page URL from the HAR pages section
page_path = None
pages = har['log'].get('pages', [])
for p in pages:
title = p.get('title', '')
parsed = urlparse(title)
if parsed.path and parsed.path != '/':
page_path = parsed.path
break
# Fallback: extract from referer headers
if not page_path:
for e in entries:
for h in e['request']['headers']:
if h['name'].lower() == 'referer':
parsed = urlparse(h['value'])
if parsed.path and parsed.path != '/':
page_path = parsed.path
break
if page_path:
break
# Find the origin (first API call)
origin = None
for e in entries:
url = urlparse(e['request']['url'])
if url.path.startswith('/api/'):
origin = f"{url.scheme}://{url.netloc}"
break
# Filter to origin entries only
api_entries = [e for e in entries if origin and origin in e['request']['url']]
# Find dashboard URL if present
dashboard_uuid = None
for e in api_entries:
path = urlparse(e['request']['url']).path
m = re.search(r'/dashboards/([0-9a-f-]{36})', path)
if m:
dashboard_uuid = m.group(1)
break
# Find project UUID
project_uuid = None
for e in api_entries:
path = urlparse(e['request']['url']).path
m = re.search(r'/projects/([0-9a-f-]{36})', path)
if m:
project_uuid = m.group(1)
break
# Count request types
methods = Counter()
api_paths = Counter()
has_base64 = False
post_endpoints_needing_body_match = []
for e in api_entries:
method = e['request']['method']
path = urlparse(e['request']['url']).path
methods[method] += 1
normalized = re.sub(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', '{uuid}', path)
api_paths[f'{method} {normalized}'] += 1
if e['response']['content'].get('encoding') == 'base64':
has_base64 = True
# Find POST endpoints with multiple entries (need body-based matching)
for key, count in api_paths.items():
if key.startswith('POST') and count > 1:
post_endpoints_needing_body_match.append((key, count))
print(f"Page path: {page_path}")
print(f"Origin: {origin}")
print(f"Total API entries: {len(api_entries)}")
print(f"Project UUID: {project_uuid}")
print(f"Dashboard UUID: {dashboard_uuid}")
print(f"Has base64 content: {has_base64}")
print(f"Methods: {dict(methods)}")
print(f"POST endpoints needing body match: {post_endpoints_needing_body_match}")
print()
print("API paths:")
for p, c in sorted(api_paths.items()):
print(f" {p}: {c}")
向用户报告分析结果:
- 从 HAR 中提取的页面路径(这是捕获 HAR 时用户正在查看的 URL)
- 来源域名
- API 条目数量
- 需要基于请求体进行匹配的显著 POST 端点(例如,包含多个图表磁贴的
dashboard-chart)
步骤 2:编写定制的回放服务
根据分析结果,将回放服务写入 scripts/har-replay-server.ts。服务必须处理以下事项:
核心结构
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
仅使用 Node.js 内置模块。使用 npx tsx scripts/har-replay-server.ts <har-path> 运行。
HAR 解析规则
- 按来源筛选:仅索引与检测到的来源域名匹配的条目
- Base64 解码:检查
entry.response.content.encoding === 'base64',并使用Buffer.from(text, 'base64').toString('utf-8')解码 - 跳过待处理的轮询响应:对于
GET /api/v2/projects/{uuid}/query/{uuid}端点,解析响应体并跳过results.status === 'pending'的条目——仅保留最终的ready响应 - 将 304 规范化为 200:将
304响应作为200提供(它们在 HAR 中包含完整响应体) - 移除传输层标头:从响应标头中移除
:pseudo-headers、transfer-encoding、content-encoding、content-length
请求匹配策略
- GET 请求:按精确的
"METHOD /path?query"匹配。若匹配失败,则回退到不带查询字符串的路径。 - POST
dashboard-chart:这些请求都会访问同一个 URL,但请求体中的chartUuid不同。根据 HAR 请求体中的chartUuid建立索引,并通过解析传入请求的请求体进行匹配。 - POST
dashboard-sql-chart:采用相同模式,但使用savedSqlUuid。如果只有一个条目,则按精确路径匹配即可。 - 其他 POST 请求(例如
availableFilters):通常路径唯一,按精确路径匹配即可。 - 分析结果中显示同一路径存在多个条目的任何 POST 端点:必须使用基于请求体中某个区分字段的匹配方式。
服务行为
- 监听 3001 端口
- 记录每个请求的方法、路径以及是否匹配
- 对 404 响应返回
{"status":"error","error":{"message":"HAR replay: no matching entry"}} - 启动时打印索引的响应数量和要导航到的仪表板 URL
步骤 3:启动回放服务和前端
- 终止 3001 端口上的所有现有进程
- 在后台启动回放服务:
npx tsx scripts/har-replay-server.ts <har-path> - 启动 Vite 前端开发服务,并将其指向回放服务:
PORT=3001 pnpm -F frontend dev - 等待两个服务就绪,然后使用以下命令验证:
curl -s http://localhost:3001/api/v1/health
步骤 4:提供 URL
确认两个服务都在运行后,告知用户要在浏览器中打开的 URL。根据步骤 1 提取的页面路径构造 URL:
http://localhost:<vite-port><page-path>
例如,如果 HAR 是在 /projects/abc-123/dashboards/def-456/view 页面捕获的,则 URL 为 http://localhost:3002/projects/abc-123/dashboards/def-456/view。
注意:由于 3001 端口已被占用,Vite 可能会选择 3000 以外的端口。请检查 Vite 启动输出,确认实际使用的端口。
步骤 5:调试渲染错误
如果用户报告错误:
- 检查回放服务日志中是否有 404(缺少 HAR 条目)
- 检查响应格式问题(Base64 编码、意外的 MIME 类型)
- 修复回放服务脚本并重启
说明
- 回放服务是一次性的——它是针对特定 HAR 文件定制的,用完后可以删除
- HAR 文件可能包含会话 Cookie 和敏感数据——不要将其提交到代码库
- 离开捕获页面后,前端会对任何其他导航请求返回 404
- 如需重新捕获:Chrome DevTools > Network 标签页 > 右键单击 > “Save all as HAR with content”
评论
支持 Markdown