HarmonyOS鸿蒙Next中用了webschemehandler拦截页面请求,第一个请求index.html正常返回,但是html里的script标签或者其它静态资源没有触发webschemehandler

HarmonyOS鸿蒙Next中用了webschemehandler拦截页面请求,第一个请求index.html正常返回,但是html里的script标签或者其它静态资源没有触发webschemehandler 【问题描述】:我把h5静态资源(css/js/html等)放到应用目录下,然后用app://example.com加载本地h5,同时用了webschemehandler拦截请求,返回本地资源。

第一个请求index.html正常返回,但是html里的script标签或者其它静态资源没有触发webschemehandler。

【问题现象】使用http作为scheme可以正常加载本地js资源,使用app作为scheme不行

【版本信息】:暂无 【复现代码】:https://atomgit.com/imzongren/WebViewDemo 【尝试解决方案】:暂无


更多关于HarmonyOS鸿蒙Next中用了webschemehandler拦截页面请求,第一个请求index.html正常返回,但是html里的script标签或者其它静态资源没有触发webschemehandler的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

开发者您好,目前暂不支持自定义协议访问本地资源,您可以参考如下解决方案:

解决Web组件本地资源跨域问题:在使用Web组件加载本地离线资源时,Web组件会拦截file协议和resource协议的跨域访问。所以在本地的index.html调用js文件时,需要通过设置一个路径列表,再使用file协议访问该路径列表中的资源,允许跨域访问本地文件。资源文件不能放在rawfile中,需要在resources目录下创建resfile,然后将index.html和vue.global.js导入。

示例代码如下:

//Index.ets
import { webview } from '@kit.ArkWeb'
import { AppSchemeHandler } from '../entryability/AppSchemeHandler'
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct Index {
  appSchemeHandler?: AppSchemeHandler
  controller: webview.WebviewController = new webview.WebviewController()
  appScheme:string = "app"//not working if change this to "app"
  uiContext: UIContext = this.getUIContext();
  baseUrl:string = 'file://' + this.uiContext.getHostContext()!.resourceDir + '/index.html'

  build() {
    NavDestination() {
      Scroll() {
        Web({
          src: "",
          controller: this.controller,
        })
          .fileAccess(true)
          .javaScriptAccess(true)
          .domStorageAccess(true)
          .onlineImageAccess(true)
          .imageAccess(true)
          .geolocationAccess(true)
          .zoomAccess(true)
          .databaseAccess(true)
          .width('100%')
          .height('100%')
          .onControllerAttached(()=> {
            console.info('onControllerAttached')
            try {
              // 设置允许可以跨域访问的路径列表
              this.controller.setPathAllowingUniversalAccess([
                this.uiContext.getHostContext()!.resourceDir,
              ]);
            } catch (error) {
              console.error(
                `ErrorCode: ${(error as BusinessError).code},  Message: ${(error as   BusinessError).message}`);
            }

          })
      }
      .width('100%')
      .height('100%')
      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
    }
    .title("WebViewDemoTest")
    .onReady((context) => {
      setTimeout(() => {
        this.initialize()
      }, 0)
      console.info("WebViewDemoTest", "Navigation onReady")
    })
    .onBackPressed(() => {
      try {
        if (this.controller?.accessBackward()) {
          this.controller?.backward()
          return true
        }
      } catch (error) {
        console.error(error)
      }
      return false
    })
    .height('100%')
    .width('100%')
  }

  private initialize() {
    this.appSchemeHandler = new AppSchemeHandler(this.getUIContext().getHostContext()!, this.baseUrl)
    this.controller.enableSafeBrowsing(false)
    this.controller.setWebSchemeHandler(this.appScheme, this.appSchemeHandler)
    this.controller.loadUrl(this.baseUrl)

  }
}

@Builder
export function IndexBuilder(name: string, param: Object) {
  Index()
}
// AppSchemehandle.ets
import { WebNetErrorList, webview } from '@kit.ArkWeb'
import { Context } from '@kit.AbilityKit'
import { FileUtil } from './ResFileUtil'

export class AppSchemeHandler extends webview.WebSchemeHandler {
  private baseUrl:string
  private contextRef:WeakRef<Context>

  private onRequestStartCallback = (request: webview.WebSchemeHandlerRequest, handler: webview.WebResourceHandler) => {
    if ( request.getRequestMethod() == 'GET' && request.getRequestUrl() &&
    request.getRequestUrl().startsWith(this.baseUrl)) {
      let url = request.getRequestUrl()
      let requestFile = url.slice(this.baseUrl.length)
      let hashIndex = requestFile.indexOf("#")
      if (hashIndex >= 0) {
        requestFile = requestFile.slice(0, hashIndex)
      }
      console.log("WebViewDemoTest", "AppSchemeHandler onRequestStart", url)
      // if (requestFile == '' || requestFile == '/') {
      //   requestFile = 'index.html'
      // }
      // if (requestFile.startsWith("/")) {
      //   requestFile = requestFile.slice(1)
      // }
      const filePath = this.baseUrl;
      if (FileUtil.exists(filePath)) {
        try {
          let data = FileUtil.read(filePath)
          if (data) {
            let response = new webview.WebSchemeHandlerResponse()
            response.setNetErrorCode(WebNetErrorList.NET_OK)
            response.setStatus(200)
            response.setStatusText("OK")
            response.setMimeType(FileUtil.getMimeType(filePath))
            response.setEncoding("utf-8")
            handler.didReceiveResponse(response)
            handler.didReceiveResponseBody(data)
            handler.didFinish()
            console.log("WebViewDemoTest", "send file", requestFile, data.byteLength)
            return true
          }
        } catch (error) {
          console.error("WebViewDemoTest", "Failed to get local file", requestFile, error)
        }
      }
    }

    return false
  }

  private onRequestStopCallback = (request: webview.WebSchemeHandlerRequest) => {
    console.info("WebViewDemoTest", "[schemeHandler] onRequestStop")
  }

  constructor(context:Context, baseUrl:string) {
    super()
    this.contextRef = new WeakRef<Context>(context)
    this.baseUrl = baseUrl
    this.onRequestStart(this.onRequestStartCallback)
    this.onRequestStop(this.onRequestStopCallback)
  }

}

如果您还是想要实现自定义协议访问本地资源,需要您提供如下信息:

请问您是在什么样的业务场景中使用该能力,交互流程是怎样的,在哪一个环节遇到了问题?方便说明能力不满足可能带来的影响:什么时间用到?是否高频?有无三方库可以做到?若提供该能力,是否会造成大工作量返工?请您注意提供的内容不要包含您或第三方的非公开信息,如给您带来不便,敬请谅解。

更多关于HarmonyOS鸿蒙Next中用了webschemehandler拦截页面请求,第一个请求index.html正常返回,但是html里的script标签或者其它静态资源没有触发webschemehandler的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


loadUrl 有好几种加载本地资源的方法,你的确是另辟蹊径啊!

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-webview-webviewcontroller#loadurl

实在不行还有loadData方法可以试试~

如果是因为引入了css和script标签导致白屏的话,最大的可能性是应为’#'这个特殊字符导致的。

详见loadData开发文档里的注释:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-webview-webviewcontroller#loaddata

这个schemehandler是官方api啊😄,主要问题是这个schemehandler和http一起用没问题,可以把静态资源替换成本地资源,和app这种自定义scheme一起用就不行(这种情况下,第一个index.html可以加载,后续的js/css不行),

看了下你的demo,如果要用自定义协议的话,有几个关键点:

  1. 需要通过customizeSchemes接口使自定义协议生效,关键代码: scheme: webview.WebCustomScheme = { schemeName: 'app', isSupportCORS: true, isSupportFetch: true }; webview.WebviewController.customizeSchemes([this.scheme]);

  2. 对应的html中引用静态资源的方式也要修改,改成 <script src="app://vue.global.js"></script> 这种格式;

另外即使都改了,你的工程中拷贝的功能还会报错,这是因为前端工程中用到了navigator.clipboard这个接口,这个接口需要在安全的上下文(如https协议)环境下才能正常使用。

所以可以直接用setPathAllowingUniversalAccess接口指定一个允许跨域访问的路径即可运行,这个比较方便。

好的,我试试😆,

在HarmonyOS Next中,WebSchemeHandler拦截页面请求后,仅处理主文档(如index.html)。HTML内的script标签、CSS、图片等子资源请求默认不会触发WebSchemeHandler拦截。这是Web组件资源加载的默认行为。

在HarmonyOS Next中,WebSchemeHandler默认只拦截主文档(如index.html)的请求。对于HTML内引用的子资源(如script、css、image等),需要额外配置才能被拦截处理。

关键点在于:Web组件默认不会将自定义scheme(如app://)的子资源请求转发给WebSchemeHandler。你需要通过WebResourceRequestsetRequestUrlMatchRule()方法,显式设置URL匹配规则来覆盖这些子资源。

在你的场景中,可以尝试以下方案:

  1. 为WebSchemeHandler设置更通用的URL匹配规则: 在注册WebSchemeHandler时,使用通配符或更宽松的匹配规则来捕获所有app://开头的请求,包括子资源。

    let schemeHandler: web.WebSchemeHandler = new web.WebSchemeHandler();
    // 设置匹配规则,拦截所有以app://example.com开头的请求
    schemeHandler.setRequestUrlMatchRule('app://example.com/*');
    webView.setWebSchemeHandler('app', schemeHandler);
    
  2. 确保资源路径正确: 检查HTML中引用的子资源路径。如果使用的是相对路径(如./js/app.js),浏览器会基于当前页面的URL(app://example.com/page.html)来构建完整请求URL(app://example.com/js/app.js)。你的WebSchemeHandler需要能匹配到这些衍生URL。

  3. 检查资源加载日志: 在WebSchemeHandler的onRequest()方法中,打印或日志记录所有进入的请求URL,确认子资源请求是否到达以及其具体的URL格式。这有助于验证匹配规则是否正确。

  4. 对比httpapp scheme的行为差异: 你提到使用http scheme正常,而app scheme异常。这通常是因为Web组件对标准scheme(http/https)和自定义scheme的处理逻辑存在差异。自定义scheme通常需要更明确的授权和配置。

核心是确保WebSchemeHandler的URL匹配规则能够覆盖到所有需要拦截的子资源请求路径。由于Web组件对自定义scheme的资源加载有更严格的限制,可能需要更全面的路径匹配规则,而非仅匹配主文档URL。

回到顶部