
本文介绍一种在前端动态加载用户提交的 html 页面后,自动扫描其所有外部资源(如 css、js、图片、视频等),并统计返回 http 400 类错误状态(如 404、403、400 等客户端错误)的资产数量的专业方案,兼顾跨域限制与实际可执行性。
本文介绍一种在前端动态加载用户提交的 html 页面后,自动扫描其所有外部资源(如 css、js、图片、视频等),并统计返回 http 400 类错误状态(如 404、403、400 等客户端错误)的资产数量的专业方案,兼顾跨域限制与实际可执行性。
在 Web IDE 类平台中,仅靠服务端 file_exists()(如 PHP)校验静态路径虽简单高效,但无法反映真实浏览器环境下的资源加载行为——例如:路径存在但权限不足(403)、重写规则导致 400/405、CDN 缓存异常、或资源依赖动态生成 URL(如带签名参数的图片链接)。因此,必须在浏览器上下文中发起真实请求并捕获响应状态。
然而需明确前提限制:
✅ 可可靠检测同源资源(如 /css/app.css、./images/logo.png);
❌ 无法获取跨域资源的精确 HTTP 状态码(CORS 未暴露 status 时,fetch() 或 XMLHttpRequest 仅返回 0 或抛错);
⚠️ 、<script>、<link> 等标签的 onerror 事件仅能感知加载失败,<strong>无法区分是 400、404、网络中断还是 CORS 拒绝。</script>
推荐方案:混合策略(服务端预检 + 前端运行时验证)
1. 服务端预检(推荐优先使用)
利用 PHP 的 get_headers() 或 curl_getinfo() 获取资源真实状态码,规避前端限制:
function getHttpStatusCode($url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false,
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code;
}
// 示例:检查 index.html 中提取的 asset URLs
$assets = ['css/style.css', 'js/main.js', 'images/icon.svg'];
$errors = array_filter($assets, function($path) {
$fullUrl = 'https://your-platform.com/user-project/' . ltrim($path, '/');
return in_array(getHttpStatusCode($fullUrl), [400, 401, 403, 404, 405]);
});
echo "共发现 " . count($errors) . " 个异常资源";✅ 优势:精准、可控、不受 CORS 影响;
⚠️ 注意:需确保 PHP 有外网访问权限,且对大量资源需加缓存(如 Redis 存储 URL → status 映射,TTL 60s)避免重复请求。
2. 前端运行时补充验证(同源限定)
当资源确定为同源时,可用 fetch() 并检查 response.status:
async function validateAssetsInHtml(htmlContent) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlContent, 'text/html');
const urls = new Set();
// 提取所有可能的资源 URL
doc.querySelectorAll('link[rel="stylesheet"], script[src], img[src], video[src], source[src]')
.forEach(el => {
const src = el.getAttribute('src') || el.getAttribute('href');
if (src && !src.startsWith('data:') && !src.startsWith('blob:')) {
urls.add(new URL(src, window.location.origin).href);
}
});
const errorCount = await Promise.all(
Array.from(urls).map(async url => {
try {
const res = await fetch(url, { method: 'HEAD', cache: 'no-store' });
return res.status >= 400 && res.status < 500 ? 1 : 0;
} catch (e) {
// 网络错误、CORS、DNS 失败等统一计为异常
return 1;
}
})
).then(results => results.reduce((a, b) => a + b, 0));
return { total: urls.size, errors: errorCount };
}
// 使用示例(点击 “Validate” 按钮触发)
document.getElementById('validate-btn').addEventListener('click', async () => {
const html = await fetch('/api/get-user-html?id=123').then(r => r.text());
const result = await validateAssetsInHtml(html);
alert(`验证完成:${result.errors}/${result.total} 个资源返回客户端错误状态`);
});关键注意事项
-
不要依赖
统计 400 错误:它无法区分 404 和跨域拒绝,且对 CSS/JS 失败无感知;
- 避免在页面内直接 eval() 或 document.write() 用户 HTML:存在严重 XSS 风险,应使用沙箱 iframe 或 DOMParser 安全解析;
- HEAD 请求更高效:相比 GET,HEAD 不下载响应体,适合状态码探测;
- 合理设置超时与重试:网络波动可能导致误报,建议单请求超时 ≤ 3s,失败后可重试 1 次。
综上,生产环境应以服务端预检为主(PHP curl_getinfo),前端运行时验证为辅(仅限同源资源)。二者结合,既保证准确性,又覆盖真实渲染链路,真正实现“所见即所验”。
立即学习“前端免费学习笔记(深入)”;











