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
开发者您好,目前暂不支持自定义协议访问本地资源,您可以参考如下解决方案:
解决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 有好几种加载本地资源的方法,你的确是另辟蹊径啊!
实在不行还有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,如果要用自定义协议的话,有几个关键点:
-
需要通过customizeSchemes接口使自定义协议生效,关键代码:
scheme: webview.WebCustomScheme = { schemeName: 'app', isSupportCORS: true, isSupportFetch: true }; webview.WebviewController.customizeSchemes([this.scheme]); -
对应的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。你需要通过WebResourceRequest的setRequestUrlMatchRule()方法,显式设置URL匹配规则来覆盖这些子资源。
在你的场景中,可以尝试以下方案:
-
为WebSchemeHandler设置更通用的URL匹配规则: 在注册WebSchemeHandler时,使用通配符或更宽松的匹配规则来捕获所有
app://开头的请求,包括子资源。let schemeHandler: web.WebSchemeHandler = new web.WebSchemeHandler(); // 设置匹配规则,拦截所有以app://example.com开头的请求 schemeHandler.setRequestUrlMatchRule('app://example.com/*'); webView.setWebSchemeHandler('app', schemeHandler); -
确保资源路径正确: 检查HTML中引用的子资源路径。如果使用的是相对路径(如
./js/app.js),浏览器会基于当前页面的URL(app://example.com/page.html)来构建完整请求URL(app://example.com/js/app.js)。你的WebSchemeHandler需要能匹配到这些衍生URL。 -
检查资源加载日志: 在WebSchemeHandler的
onRequest()方法中,打印或日志记录所有进入的请求URL,确认子资源请求是否到达以及其具体的URL格式。这有助于验证匹配规则是否正确。 -
对比
http与appscheme的行为差异: 你提到使用httpscheme正常,而appscheme异常。这通常是因为Web组件对标准scheme(http/https)和自定义scheme的处理逻辑存在差异。自定义scheme通常需要更明确的授权和配置。
核心是确保WebSchemeHandler的URL匹配规则能够覆盖到所有需要拦截的子资源请求路径。由于Web组件对自定义scheme的资源加载有更严格的限制,可能需要更全面的路径匹配规则,而非仅匹配主文档URL。


