如何在动态加载脚本时检测403等错误状态码?
如何捕获动态加载脚本的HTTP错误状态码?
你碰到的这个问题我之前也遇到过——try/catch完全没反应,onerror又拿不到具体的状态码,确实挺头疼的。核心原因是:把<script>插入DOM的同步操作本身不会抛出网络错误,真正的HTTP错误是在后续异步的请求过程中发生的,所以同步的try/catch根本抓不到它,而原生的script.onerror事件确实不会直接返回状态码信息。
下面给你两个实用的解决方案,你可以根据场景选择:
方案1:先用Fetch API做预检请求(推荐)
这个思路是先通过fetch请求脚本的URL,直接拿到HTTP响应的状态码,确认请求没问题后再创建脚本元素插入DOM。这样既能精准捕获403、404这类状态码,还能提前给用户反馈。
优点是兼容性好,状态码获取稳定;唯一的小缺点是会多一次HTTP请求,但用HEAD方法的话只会请求响应头,不会下载脚本内容,开销很小,而且浏览器缓存会帮你处理重复请求的问题。
改造后的代码示例:
private _scripts: { [key: string]: Subject<Event> } = {}; load(url: string): Observable<Event> { if (this._scripts[url]) { return this._scripts[url].asObservable(); } const subject = new ReplaySubject<Event>(1); this._scripts[url] = subject; // 用HEAD请求预检,获取状态码(也可以用GET,HEAD更轻量) fetch(url, { method: 'HEAD' }) .then(response => { if (!response.ok) { // 这里可以根据状态码自定义错误信息 const error = new Error(`脚本加载失败:${response.status} ${response.statusText}`); // 给错误对象附加状态码属性,方便上层处理 (error as any).statusCode = response.status; subject.error(error); return; } // 预检通过,创建脚本元素加载实际内容 const script = this.document.createElement("script"); script.src = url; script.onload = (event: Event) => { subject.next(event); subject.complete(); }; script.onerror = (error: string | Event) => { subject.error(error); }; this.document.head.appendChild(script); }) .catch(fetchError => { // 捕获网络层面的错误,比如断网、跨域问题 subject.error(fetchError); }); return subject.asObservable(); }
方案2:利用Performance API获取请求状态
如果不想多一次请求,可以在onerror触发后,通过浏览器的Performance API去查找对应请求的状态记录。不过这个方法有个限制:跨域场景下,如果服务器没有配置合适的CORS头,responseStatus会返回0,拿不到具体状态码。
代码示例:
private _scripts: { [key: string]: Subject<Event> } = {}; load(url: string): Observable<Event> { if (this._scripts[url]) { return this._scripts[url].asObservable(); } const subject = new ReplaySubject<Event>(1); this._scripts[url] = subject; const script = this.document.createElement("script"); script.src = url; script.onload = (event: Event) => { subject.next(event); subject.complete(); }; script.onerror = () => { // 查找对应URL的性能记录 const entries = performance.getEntriesByName(url) as PerformanceResourceTiming[]; if (entries.length > 0) { const lastEntry = entries[entries.length - 1]; const statusCode = lastEntry.responseStatus; let errorMsg = '脚本加载失败'; // 根据状态码补充具体提示 if (statusCode) { errorMsg += `:${statusCode}${statusCode === 403 ? '(未授权)' : ''}`; } subject.error(new Error(errorMsg)); } else { subject.error(new Error('脚本加载失败,无法获取错误详情')); } }; this.document.head.appendChild(script); return subject.asObservable(); }
最后补充两句:
- 优先选方案1,状态码获取更可靠,尤其是跨域场景;
- 如果用方案2,记得测试跨域环境下的表现,确保服务器配置了
Timing-Allow-Origin头,否则拿不到状态码。
内容的提问来源于stack exchange,提问作者Georgi Koemdzhiev
相关产品推荐
相关产品推荐

