开天辟地 HarmonyOS(鸿蒙) - 组件(webview): Web(拦截)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 组件(webview): Web(拦截)

示例如下:

pages\component\webview\WebDemo2.ets

/*
 * Web - 用于显示网页的组件
 * 本例用于演示 webview 的各种拦截
 */

import { TitleBar, MyLog } from '../../TitleBar';
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
import { http } from '@kit.NetworkKit';
import { promptAction } from '@kit.ArkUI';

webview.once("webInited", () => {
  MyLog.d("webInited");
})

@Entry
@Component
struct WebDemo2 {

  build() {
    Column() {
      TitleBar()
      Tabs() {
        TabContent() { MySample1() }.tabBar('alert/confirm/prompt/console 拦截').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('请求拦截,然后决定是否允许请求').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('请求拦截,然后下发自定义响应').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
      .layoutWeight(1)
    }
  }
}


// 模拟 js 的 prompt 对话框
@CustomDialog
struct MyCustomDialog {
  controller?: CustomDialogController
  okClicked: (inputMessage: string) => void = (inputMessage: string) => {  }
  cancelClicked: () => void = () => {  }

  @State inputMessage: string = ""

  build() {
    Column({space:10}) {
      Text("onPrompt")
      TextInput({ text: $$this.inputMessage })
      Button('确认')
        .onClick(() => {
          this.okClicked(this.inputMessage)
        })
      Button('取消')
        .onClick(() => {
          this.cancelClicked()
        })
    }
  }
}
@Component
struct MySample1 {

  @State message: string = ""
  controller: webview.WebviewController = new webview.WebviewController();

  openPromptDialog(event:OnConfirmEvent) {
    let dialogController: CustomDialogController = new CustomDialogController({
      builder: MyCustomDialog({
        okClicked: (inputMessage)=> {
          // event.result.handlePromptConfirm() 点击了 prompt 的确认按钮,并传递用户输入的数据
          event.result.handlePromptConfirm(inputMessage)
          dialogController.close()
        },
        cancelClicked: ()=> {
          // event.result.handleCancel() 点击了 prompt 的取消按钮
          event.result.handleCancel()
          dialogController.close()
        },
      }),
      autoCancel: false,
    })
    dialogController.open()
  }

  build() {
    Column({space:10}) {

      Text(this.message)

      /*
       * Web - 用于显示网页的组件
       *   src - 网页地址(支持网络地址,也支持 file:// 沙箱地址,也支持 $rawfile 或 resource 资源地址)
       *   controller - 绑定的 WebviewController 对象
       *   javaScriptAccess() - 是否允许执行 javascript 脚本
       *   domStorageAccess() - 是否允许使用本地存储(localStorage 和 sessionStorage)
       *   fileAccess() - 是否允许访问文件系统(注:$rawfile 地址不受此限制)
       *   imageAccess() - 是否允许加载图片资源
       *   onConsole(), onAlert(), onConfirm(), onPrompt() - 拦截 js 的 console, alert, confirm, prompt
       *     return false 代表没处理,需要执行默认的行为
       *     return true 代表已处理,不需要执行默认的行为
       *
       * 注:本例相关的 html 参见 /entry/src/main/resources/rawfile/html2.html
       */
      Web({ src: $rawfile('html2.html'), controller: this.controller })
        .javaScriptAccess(true)
        .domStorageAccess(true)
        .fileAccess(true)
        .imageAccess(true)
        .onConsole((event) => {
          /*
           * getMessageLevel() - console 的级别(debug 是 1,info 是 2,warn 是 3,error 是 4)
           * getMessage() - console 的信息
           * getSourceId() - 调用 console 的 url
           * getLineNumber() - 调用 console 的代码的所在行
           */
          this.message += `onConsole level:${event.message.getMessageLevel()}, message:${event.message.getMessage()}, url:${event.message.getSourceId()}, line:${event.message.getLineNumber()}\n`
          return true
        })
        .onAlert((event) => {
          AlertDialog.show({
            title: 'onAlert',
            message: event.message,
            autoCancel: false,
            secondaryButton: {
              value: '确认',
              // event.result.handleConfirm() 点击了 alert 的确认按钮
              action: () => { event.result.handleConfirm(); }
            }
          })
          return true
        })
        .onConfirm((event) => {
          AlertDialog.show({
            title: 'onConfirm',
            message: this.message,
            autoCancel: false,
            primaryButton: {
              value: '取消',
              // event.result.handleCancel() 点击了 confirm 的取消按钮
              action: () => { event.result.handleCancel(); }
            },
            secondaryButton: {
              value: '确认',
              // event.result.handleConfirm() 点击了 confirm 的确认按钮
              action: () => { event.result.handleConfirm(); }
            },
          })
          return true;
        })
        .onPrompt((event) => {
          this.openPromptDialog(event)
          return true;
        })
    }
  }
}


@Component
struct MySample2 {

  @State message: string = ""
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column({space:10}) {

      Text(this.message)

      /*
       * Web - 用于显示网页的组件
       *   onLoadIntercept(), onOverrideUrlLoading() - 请求 url 之前的回调,可以决定是否允许此次请求
       *     return true - 代表已处理,不需要执行默认的行为
       *     return false - 代表没处理,需要执行默认的行为
       *
       * WebResourceRequest - 请求对象
       *   getRequestUrl() - 此次请求的 url
       *   getRequestHeader() - 此次请求的 header
       *   getRequestMethod() - 此次请求的 method
       *   isRedirect() - 此次请求是否被服务端重定向
       *   isMainFrame() - 此次请求是否为主 frame 发起
       */
      Web({ src: $rawfile('html3.html'), controller: this.controller })
        .onLoadIntercept((event) => {
          let webResourceRequest: WebResourceRequest = event.data // 获取此次请求的 WebResourceRequest 对象
          const url: string = webResourceRequest.getRequestUrl()
          this.message += `onLoadIntercept: ${url}\n`
          // 允许此次请求,不允许的话则 return true 即可
          return false
        })
        .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => {
          const url: string = webResourceRequest.getRequestUrl()
          this.message += `onOverrideUrlLoading: ${url}\n`
          // 允许此次请求,不允许的话则 return true 即可
          return false
        })
    }
  }
}


@Component
struct MySample3 {

  @State message: string = ""
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column({space:10}) {

      Text(this.message)

      /*
       * Web - 用于显示网页的组件
       *   onInterceptRequest() - 拦截所有请求,并根据需要使用默认响应(return null),或使用自定义响应(return 一个 WebResourceResponse 对象)
       *
       * WebResourceRequest - 请求对象
       *   getRequestUrl() - 此次请求的 url
       *   getRequestHeader() - 此次请求的 header
       *   getRequestMethod() - 此次请求的 method
       *   isRedirect() - 此次请求是否被服务端重定向
       *   isMainFrame() - 此次请求是否为主 frame 发起
       *
       * WebResourceResponse - 自定义响应对象
       *   setResponseData() - 响应的数据
       *   setResponseHeader() - 响应头
       *   setResponseMimeType() - 响应的 mime 类型
       *   setResponseEncoding() - 响应的编码方式
       *   setResponseCode(), setReasonMessage() - 响应的状态码,和此状态码的说明
       *   setResponseIsReady() - 用于说明当前响应是否已经准备就绪
       *     true - 返回 WebResourceResponse 对象后,立即下发响应
       *     false - 返回 WebResourceResponse 对象后,等待,直到设置为 true 才下发响应
       */
      Web({ src: $rawfile('html3.html'), controller: this.controller })
        .onInterceptRequest((event) => {
          let webResourceRequest: WebResourceRequest = event.request // 获取此次请求的 WebResourceRequest 对象

          // 使用本地资源作为自定义响应
          if (webResourceRequest.getRequestUrl() == "http://aaa.bbb.ccc/abc.jpg") {
            let response = new WebResourceResponse();
            response.setResponseData($rawfile('son.jpg'))
            response.setResponseMimeType("image/jpeg")
            response.setResponseCode(200)
            response.setReasonMessage('OK')
            response.setResponseIsReady(true)
            return response
          }
          // 使用网络资源作为自定义响应
          else if (webResourceRequest.getRequestUrl() == "http://aaa.bbb.ccc/xyz.jpg") {
            let response = new WebResourceResponse()
            response.setResponseIsReady(false) // 先要设置为 false
            let httpRequest = http.createHttp()
            httpRequest.request(
              "https://res-static.hc-cdn.cn/cloudbu-site/china/zh-cn/hdhomeportal/logonew.png",
              {
                method: http.RequestMethod.GET,
                expectDataType: http.HttpDataType.ARRAY_BUFFER,
              },
              (err: BusinessError, data: http.HttpResponse) => {
                response.setResponseData(data.result as ArrayBuffer)
                response.setResponseMimeType('image/png')
                response.setResponseCode(200)
                response.setReasonMessage('OK')
                response.setResponseIsReady(true) // 网络资源请求成功后,并构造好了 WebResourceResponse 对象,则设置为 true
              }
            );
            return response;
          }
          // 使用自定义 html 作为自定义响应
          else if (webResourceRequest.getRequestUrl() == "http://developer.huawei.com/") {
            let response = new WebResourceResponse()
            response.setResponseData('<p style="font-size: 64px;">hello webabcd</p>')
            response.setResponseEncoding('utf-8')
            response.setResponseMimeType("text/html")
            response.setResponseCode(200)
            response.setReasonMessage('OK')
            response.setResponseIsReady(true)
            return response;
          }
          // 可以拦截自定义协议跳转
          else if (webResourceRequest.getRequestUrl() == "custom://a.b.c/") {
            promptAction.showToast({
              message: "custom://a.b.c/",
              duration: 3000
            })
            return null;
          }

          // 使用默认响应
          return null;
        })
    }
  }
}

\entry\src\main\resources\rawfile\html2.html

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>

<p>Hello World</p>

<div id="div"></div>

<div>
    <button onclick="alertSample();">alert</button>
</div>

<div>
    <button onclick="confirmSample();">confirm</button>
</div>

<div>
    <button onclick="promptSample();">prompt</button>
</div>

<div>
    <button onclick="consoleSample();">console</button>
</div>

<script>

    // 用于演示在 webview 中使用本地存储
    localStorage.setItem("name", "webabcd");
    const name = localStorage.getItem("name");
    document.getElementById('div').innerHTML = `name: ${name}<br/>`

    // 用于演示在 webview 中拦截 alert
    function alertSample() {
        alert("i am alert")
        document.getElementById('div').innerHTML += `alert ok<br/>`
    }

    // 用于演示在 webview 中拦截 confirm
    function confirmSample() {
        let r = confirm("i am confirm");
        if (r == true) {
            document.getElementById('div').innerHTML += `confirm ok<br/>`
        } else {
            document.getElementById('div').innerHTML += `confirm cancel<br/>`
        }
    }

    // 用于演示在 webview 中拦截 prompt
    function promptSample() {
        let message = prompt("i am prompt");
        if (message != null) {
            document.getElementById('div').innerHTML += `prompt ${message}<br/>`
        } else {
            document.getElementById('div').innerHTML += `prompt cancel<br/>`
        }
    }

    // 用于演示在 webview 中拦截 console
    function consoleSample() {
        console.debug("console debug")
        console.info("console info")
        console.warn("console warn")
        console.error("console error")
    }

</script>
</body>
</html>

\entry\src\main\resources\rawfile\html3.html

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>

<div>
    <img src="http://aaa.bbb.ccc/abc.jpg" />
</div>

<div>
    <img src="http://aaa.bbb.ccc/xyz.jpg" />
</div>

<div>
    <a href="http://developer.huawei.com/">点我跳转 http</a>
</div>

<div>
    <a href="custom://a.b.c/">点我跳转自定义协议</a>
</div>

</body>
</html>

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-04-15 09:37  webabcd  阅读(106)  评论(0)    收藏  举报