Handling Cross-Origin Requests for Local Resources in HarmonyOS Next
Intercepting Cross-Origin Requests
To enhance security, the ArkWeb kernel does not allow the file
or resource
protocols to access requests from cross-origin contexts. Therefore, when using Web components to load local offline resources, the Web component will intercept cross-origin access for the file
and resource
protocols. Developers can resolve this by setting a path list and using the file
protocol to access resources within that list, allowing cross-origin access to local files.
When the Web component cannot access local cross-origin resources, developers can see error messages similar to the following in the DevTools console:
Access to script at 'xxx' from origin 'xxx' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, arkweb, data, chrome-extension, chrome, https, chrome-untrusted.
Solutions for Cross-Origin Requests to Local Resources
Method 1: Using HTTP or HTTPS Protocols
To enable the Web component to successfully access cross-origin resources, developers should use http
or https
protocols instead of the file
or resource
protocols. The URL domain should be a custom domain constructed for personal or organizational use to avoid conflicts with actual domains on the internet. Additionally, developers need to use the onInterceptRequest
method of the Web component to intercept and replace local resources.
The following example demonstrates how to resolve the issue of failed cross-origin access to local resources. In this example, index.html
and js/script.js
are placed in the rawfile
directory of the project. If the resource
protocol is used to access index.html
, js/script.js
will be intercepted due to cross-origin restrictions and will fail to load. In the example, the https://www.example.com/
domain replaces the original resource
protocol, and the onInterceptRequest
interface is used to replace the resources, allowing js/script.js
to load successfully and resolving the cross-origin interception issue.
// main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
webviewController: webview.WebviewController = new webview.WebviewController();
// Construct a mapping table of domain names and local files
schemeMap = new Map([
["https://www.example.com/index.html", "index.html"],
["https://www.example.com/js/script.js", "js/script.js"],
])
// Construct a mapping table of local files and their corresponding mimeType
mimeTypeMap = new Map([
["index.html", 'text/html'],
["js/script.js", "text/javascript"]
])
build() {
Row() {
Column() {
// For local index.html, use http or https protocol instead of file or resource protocol, and construct a custom domain.
// In this example, www.example.com is used.
Web({ src: "https://www.example.com/index.html", controller: this.webviewController })
.javaScriptAccess(true)
.fileAccess(true)
.domStorageAccess(true)
.geolocationAccess(true)
.width("100%")
.height("100%")
.onInterceptRequest((event) => {
if (!event) {
return;
}
// Match and replace local offline resources to bypass cross-origin restrictions
if (this.schemeMap.has(event.request.getRequestUrl())) {
let rawfileName: string = this.schemeMap.get(event.request.getRequestUrl())!;
let mimeType = this.mimeTypeMap.get(rawfileName);
if (typeof mimeType === 'string') {
let response = new WebResourceResponse();
// Construct response data; if the local file is in rawfile, set it as follows
response.setResponseData($rawfile(rawfileName));
response.setResponseEncoding('utf-8');
response.setResponseMimeType(mimeType);
response.setResponseCode(200);
response.setReasonMessage('OK');
response.setResponseIsReady(true);
return response;
}
}
return null;
})
}
.width('100%')
}
.height('100%')
}
}
<!-- main/resources/rawfile/index.html -->
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<script crossorigin src="./js/script.js"></script>
</body>
</html>
// main/resources/rawfile/js/script.js
const body = document.body;
const element = document.createElement('div');
element.textContent = 'success';
body.appendChild(element);
Method 2: Setting a Path List with setPathAllowingUniversalAccess
Use the setPathAllowingUniversalAccess
method to set a path list. When using the file
protocol to access resources in this list, cross-origin access to local files is allowed. Additionally, once the path list is set, the file
protocol will be limited to accessing resources within the list (overriding the behavior of fileAccess
). Paths in the list must conform to one of the following formats:
-
Application file directory obtained via
Context.filesDir
, with subdirectory examples:/data/storage/el2/base/files/example
/data/storage/el2/base/haps/entry/files/example
-
Application resource directory obtained via
Context.resourceDir
, with subdirectory examples:/data/storage/el1/bundle/entry/resource/resfile
/data/storage/el1/bundle/entry/resource/resfile/example
If any path in the list does not meet the above conditions, the system will throw an exception with code 401, indicating that the path list setting has failed. If the path list is empty, the accessibility of the file
protocol will follow the rules of fileAccess
.
// main/ets/pages/Index.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct WebComponent {
controller: WebviewController = new webview.WebviewController();
build() {
Row() {
Web({ src: "", controller: this.controller })
.onControllerAttached(() => {
try {
// Set the path list allowing cross-origin access
this.controller.setPathAllowingUniversalAccess([
getContext().resourceDir,
getContext().filesDir + "/example"
])
this.controller.loadUrl("file://" + getContext().resourceDir + "/index.html")
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
.javaScriptAccess(true)
.fileAccess(true)
.domStorageAccess(true)
}
}
}
Explanation
-
Method 1: This method involves using the
https
protocol to load local resources and intercepting requests using theonInterceptRequest
method to replace local resources with custom URLs.
Top comments (0)