在 HarmonyOS 的 ArkWeb 引擎中,请求拦截是实现离线缓存、Mock 数据、广告过滤等高级功能的核心技术。本文将通过完整的实战示例,带你掌握 onInterceptRequest 的用法,并对比 onLoadIntercept 等回调,助你在后端架构与微服务场景下灵活控制 Web 组件的网络行为。
大白话讲清楚“拦截请求”是什么意思
想象一下:Web 组件加载网页时,网页向服务器发了一个 HTTP 请求。正常情况下,系统帮你发出去、等响应、渲染页面。但有了 onInterceptRequest,你可以在请求“出门之前”截住它:
- 让它继续走正常流程:返回
null,页面照常从服务器获取资源。 - 用自己的数据替换响应:构造一个
WebResourceResponse对象返回,完全绕过网络。
典型场景包括:
- 把线上资源替换成本地缓存(离线包方案)
- Mock API 数据,方便调试(类似后端开发中的中间件 Mock)
- 拦截广告/追踪请求,返回空响应
- 将
https://app等自定义协议的请求重定向到 App 内部逻辑
网页加载时会发出大量网络请求——图片、CSS、JS、API 接口……如果你想把某个请求"劫持"掉,返回自己准备好的数据, 就是那把钥匙。
与 onLoadIntercept / onOverrideUrlLoading 的区别
很多开发者容易把这三个回调搞混。简单对比一下:
| 回调 | 触发时机 | 能否修改响应体 | 典型用途 |
|---|---|---|---|
| 主框架导航前 | 不能,只能拦截/放行 | 阻止某个 URL 加载 | |
| URL 被重新加载前 | 不能 | 捕获自定义协议跳转 | |
| 任意资源请求(含主框架/子资源) | 能 | 替换响应内容、离线包 |
总结:onInterceptRequest 专注于资源请求的替换,适合静态资源缓存;onLoadIntercept 负责导航行为的阻断,适合安全策略;onOverrideUrlLoading 则用于处理自定义协议跳转。在微服务架构中,你可能会同时使用它们来构建完整的前端资源管控层。
流程图
下图清晰地展示了请求在 ArkWeb 中的完整生命周期:

️ 核心 API 一览
WebResourceRequest(请求信息)
每个被拦截的请求都会携带以下信息:
event.request.getRequestUrl() // 请求的 URL
event.request.isMainFrame() // 是否是主框架请求
event.request.isRedirect() // 是否是重定向
event.request.isRequestGesture() // 是否由用户手势触发
event.request.getRequestHeader() // 请求头列表
event.request.getRequestMethod() // 请求方法 GET/POST...
你可以通过 getRequestUrl() 获取 URL,通过 getRequestMethod() 判断是 GET 还是 POST,甚至通过 getRequestHeader() 读取自定义头部——这在对接后端服务时非常有用。
WebResourceResponse(构造自定义响应)
这是你返回给 Web 组件的“假响应”:
const response = new WebResourceResponse();
// 必须设置的四项
response.setResponseData(htmlString); // 响应体(string | ArrayBuffer | Resource)
response.setResponseMimeType('text/html'); // MIME 类型
response.setResponseEncoding('utf-8'); // 编码
response.setResponseCode(200); // HTTP 状态码
response.setReasonMessage('OK'); // 状态说明
// 可选:自定义响应头
const headers: Header[] = [
{ headerKey: 'Cache-Control', headerValue: 'no-cache' },
{ headerKey: 'Connection', headerValue: 'keep-alive' }
];
response.setResponseHeader(headers);
构造时需要注意:
- mimeType 必须准确,否则浏览器会拒绝执行(比如 JS 文件写成
text/plain会失效) - 二进制文件(图片、字体)必须用
ArrayBuffer,不要用字符串 - 状态码 默认为 200,但你可以返回 404、500 等模拟错误场景
完整示例:本地离线包拦截
这是最常见的生产级用法——把线上的 .css、.js、.png 资源替换成本地 rawfile 中的文件,做到“即使断网也能用”。
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';
@Entry
@Component
struct OfflineCachePage {
controller: webview.WebviewController = new webview.WebviewController();
// 从 rawfile 读取文件内容(同步,适合小文件)
private readRawFile(path: string): ArrayBuffer | null {
try {
// getContext().resourceManager 获取资源管理器
const rm = getContext(this).resourceManager;
return rm.getRawFileContentSync(path).buffer as ArrayBuffer;
} catch (e) {
console.error('readRawFile error:', (e as BusinessError).message);
return null;
}
}
build() {
Column() {
Web({ src: 'https://example.com', controller: this.controller })
.onInterceptRequest((event) => {
if (!event) return null;
const url = event.request.getRequestUrl();
console.info('[Intercept] url:', url);
// 只拦截 /static/ 路径下的资源
if (url.includes('/static/')) {
// 把 URL 映射到本地 rawfile 路径
// 例如 https://example.com/static/js/app.js → rawfile/static/js/app.js
const localPath = url.replace('https://example.com/', '');
const data = this.readRawFile(localPath);
if (data) {
const response = new WebResourceResponse();
response.setResponseData(data);
response.setResponseCode(200);
response.setReasonMessage('OK');
// 根据文件后缀设置 mimeType
if (url.endsWith('.js')) {
response.setResponseMimeType('application/javascript');
} else if (url.endsWith('.css')) {
response.setResponseMimeType('text/css');
} else if (url.endsWith('.png') || url.endsWith('.jpg')) {
response.setResponseMimeType('image/png');
} else {
response.setResponseMimeType('application/octet-stream');
}
response.setResponseEncoding('utf-8');
return response; // 返回自定义响应,不走网络
}
}
return null; // 其他请求走正常网络
})
.onPageEnd((event) => {
if (event) console.info('页面加载完成:', event.url);
})
}
.width('100%')
.height('100%')
}
}
此方案类似后端架构中的本地缓存层,能显著降低服务端压力,提升首屏加载速度。记得将大文件提前加载到内存,避免在回调中执行耗时 I/O 操作。
示例2:拦截并返回动态 HTML 字符串
有时候你想给某个特定 URL 返回一段自己生成的 HTML,比如嵌入一个本地配置页:
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct MockApiPage {
controller: webview.WebviewController = new webview.WebviewController();
// 模拟的 API 返回数据
private mockUserData: string = JSON.stringify({ name: '张三', age: 25 });
build() {
Column() {
Web({ src: 'https://myapp.com/home', controller: this.controller })
.onInterceptRequest((event) => {
if (!event) return null;
const url = event.request.getRequestUrl();
// 拦截 /api/user 接口,返回 Mock 数据
if (url.endsWith('/api/user')) {
const response = new WebResourceResponse();
response.setResponseData(this.mockUserData);
response.setResponseMimeType('application/json');
response.setResponseEncoding('utf-8');
response.setResponseCode(200);
response.setReasonMessage('OK');
// 设置 CORS 头,允许跨域
const headers: Header[] = [
{ headerKey: 'Access-Control-Allow-Origin', headerValue: '*' },
{ headerKey: 'Content-Type', headerValue: 'application/json' }
];
response.setResponseHeader(headers);
return response;
}
return null;
})
}
.width('100%')
.height('100%')
}
}
这相当于在 ArkWeb 中实现了一个微型后端接口,无需真实服务器即可动态生成页面。在微服务架构中,你可以用此方法快速开发调试页面,或注入监控脚本。
示例3:结合 onLoadIntercept 阻断特定域名
onInterceptRequest 负责修改响应,onLoadIntercept 负责完全阻断。两者配合使用效果更好:
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct BlockAndReplacePage {
controller: webview.WebviewController = new webview.WebviewController();
// 需要屏蔽的追踪域名列表
private blockedDomains: string[] = [
'tracker.example.com',
'ads.doubleclick.net',
];
private isBlocked(url: string): boolean {
return this.blockedDomains.some(domain => url.includes(domain));
}
build() {
Column() {
Web({ src: 'https://example.com', controller: this.controller })
// 主框架导航拦截
.onLoadIntercept((event) => {
if (event && this.isBlocked(event.data.getRequestUrl())) {
console.info('主框架导航被阻断:', event.data.getRequestUrl());
return true; // true = 阻止加载
}
return false;
})
// 子资源请求拦截(图片、脚本等)
.onInterceptRequest((event) => {
if (!event) return null;
const url = event.request.getRequestUrl();
if (this.isBlocked(url)) {
// 返回空响应,等于屏蔽了这个请求
const response = new WebResourceResponse();
response.setResponseData('');
response.setResponseMimeType('text/plain');
response.setResponseEncoding('utf-8');
response.setResponseCode(200);
response.setReasonMessage('OK');
return response;
}
return null;
})
}
.width('100%')
.height('100%')
}
}
⚠️ 注意:onLoadIntercept 返回 true 会阻止页面加载,适合屏蔽第三方广告域名或恶意网站。
⚠️ 注意事项与最佳实践
- 不要在回调中做耗时操作:
onInterceptRequest是同步回调,如果你在里面读大文件或做网络请求,会阻塞页面渲染。大文件建议提前加载到内存,或者使用异步WebResourceResponse方案。 - mimeType 必须准确:如果你返回 JS 文件但 mimeType 写成
text/plain,浏览器会拒绝执行。可以参考下面的速查表。 - 二进制文件用 ArrayBuffer:图片、字体等二进制资源,
setResponseData接受ArrayBuffer,别用字符串。 - 返回 null 表示不拦截:千万别手误返回
undefined,只有显式返回null才会走正常网络。
常见 MIME 类型速查
| 文件类型 | mimeType |
|---|---|
| HTML | |
| JavaScript | |
| CSS | |
| JSON | |
| PNG 图片 | |
| JPEG 图片 | |
| SVG | |
| 字体 woff2 |
保存这张表,开发时随时查阅,避免因类型错误导致资源加载失败。
小结
onInterceptRequest 是 ArkWeb 中最强大的请求控制手段,核心逻辑就两句话:
- 返回
WebResourceResponse→ 用你的数据,跳过网络 - 返回
null→ 正常走网络,不干预
配合 onLoadIntercept(阻止导航)和 onOverrideUrlLoading(捕获自定义协议),你可以对 Web 组件的网络行为做到精细的完全掌控。这套机制在后端架构中常用于实现本地缓存、Mock 服务、安全过滤等功能,是构建高性能、高可控混合应用的基石。
延伸思考:在微服务架构中,你可以将 ArkWeb 的请求拦截与后端 API 网关配合,实现统一资源管控。例如,通过拦截特定路径的请求,将其重定向到本地数据库或缓存服务,从而减少服务端压力。
[AFFILIATE_SLOT_2]onInterceptRequestonLoadInterceptonOverrideUrlLoadingonInterceptRequesttext/htmlapplication/javascripttext/cssapplication/jsonimage/pngimage/jpegimage/svg+xmlfont/woff2
浙公网安备 33010602011771号