欢迎光临
我们一直在努力

HarmonyOS ArkWeb 系列之请求拦截与自定义响应:onInterceptRequest 完整指南

文章目录

    • 大白话讲清楚"拦截请求"是什么意思
    • 与 onLoadIntercept / onOverrideUrlLoading 的区别
    • 流程图
    • 核心 API 一览
      • WebResourceRequest(请求信息)
      • WebResourceResponse(构造自定义响应)
    • 完整示例:本地离线包拦截
    • 示例2:拦截并返回动态 HTML 字符串
    • 示例3:结合 onLoadIntercept 阻断特定域名
    • 注意事项
    • 常见 MIME 类型速查
    • 小结

网页加载时会发出大量网络请求——图片、CSS、JS、API 接口……如果你想把某个请求"劫持"掉,返回自己准备好的数据,onInterceptRequest 就是那把钥匙。

大白话讲清楚"拦截请求"是什么意思

想象一下:Web 组件加载网页,网页向服务器发了一个 HTTP 请求。正常情况下,系统帮你发出去、等响应、渲染页面。

onInterceptRequest 让你在请求"出门之前"截住它:

  • 你想让它继续走正常流程:返回 null
  • 你想用自己的数据替换响应:构造一个 WebResourceResponse 对象返回

这有什么用?典型场景:

  • 把线上资源替换成本地缓存(离线包方案)
  • Mock API 数据,方便调试
  • 拦截某些广告/追踪请求,返回空数据
  • 在加载本地 HTML 时,把 fetch('/api/xxx') 的请求重定向到 App 内部逻辑
  • 与 onLoadIntercept / onOverrideUrlLoading 的区别

    很多人容易把这三个回调搞混,简单对比一下:

    回调触发时机能否修改响应体典型用途
    onLoadIntercept 主框架导航前 不能,只能拦截/放行 阻止某个 URL 加载
    onOverrideUrlLoading URL 被重新加载前 不能 捕获自定义协议跳转
    onInterceptRequest 任意资源请求(含主框架/子资源) 替换响应内容、离线包

    流程图

    核心 API 一览

    WebResourceRequest(请求信息)

    event.request.getRequestUrl() // 请求的 URL
    event.request.isMainFrame() // 是否是主框架请求
    event.request.isRedirect() // 是否是重定向
    event.request.isRequestGesture() // 是否由用户手势触发
    event.request.getRequestHeader() // 请求头列表
    event.request.getRequestMethod() // 请求方法 GET/POST…

    WebResourceResponse(构造自定义响应)

    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);

    完整示例:本地离线包拦截

    这是最常见的生产级用法——把线上的 /static/ 资源替换成本地 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%')
    }
    }

    示例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%')
    }
    }

    示例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%')
    }
    }

    注意事项

  • 不要在回调中做耗时操作:onInterceptRequest 是同步回调,如果你在里面读大文件或做网络请求,会阻塞页面渲染。大文件建议提前加载到内存,或者使用异步 ResponseDataID 方案。
  • mimeType 必须准确:如果你返回 JS 文件但 mimeType 写成 text/plain,浏览器会拒绝执行。
  • 二进制文件用 ArrayBuffer:图片、字体等二进制资源,setResponseData 接受 ArrayBuffer,别用字符串。
  • 返回 null 表示不拦截:千万别手误返回 undefined,只有显式返回 null 才会走正常网络。
  • 常见 MIME 类型速查

    文件类型mimeType
    HTML text/html
    JavaScript application/javascript
    CSS text/css
    JSON application/json
    PNG 图片 image/png
    JPEG 图片 image/jpeg
    SVG image/svg+xml
    字体 woff2 font/woff2

    小结

    onInterceptRequest 是 ArkWeb 中最强大的请求控制手段,核心逻辑就两句话:

    • 返回 WebResourceResponse → 用你的数据,跳过网络
    • 返回 null → 正常走网络,不干预

    配合 onLoadIntercept(阻止导航)和 onOverrideUrlLoading(捕获自定义协议),你可以对 Web 组件的网络行为做到精细的完全掌控。

    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS ArkWeb 系列之请求拦截与自定义响应:onInterceptRequest 完整指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址