• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Resolving Cross-Origin Resource Access
2
3## Background
4
5For security purposes, the ArkWeb kernel does not allow for cross-origin requests using the file or resource protocol in the URL context. As such, the **Web** component blocks such requests when loading local offline resources. To allow cross-origin requests using the file, you can use method 2 to set a path list. When cross-origin requests from the **Web** component are blocked, an error message similar to the following is displayed on the DevTools console:
6
7```
8Access 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.
9```
10
11## Solutions to Local Cross-Origin Resource Access
12
13- Method 1
14
15  For the **Web** component to load local resources across origins, use HTTP or HTTPS, instead of file or resource, as the protocol. The domain name of the URL to use should be one that you customize for individuals or organizations. Make sure it does not conflict with any existing domain name in the real world. You also need to use the [onInterceptRequest](../reference/apis-arkweb/ts-basic-components-web.md#oninterceptrequest9) API of the **Web** component to intercept and replace local resources.
16
17  In the following example, the **index.html** and **js/script.js** files are stored in the **rawfile** folder of the project directory. If the resource protocol is used to access **index.html**, loading **js/script.js** will fail due to cross-origin blocking. To resolve this issue, the HTTPS protocol is used instead, as in **https:\//www\.example.com/**, and the [onInterceptRequest](../reference/apis-arkweb/ts-basic-components-web.md#oninterceptrequest9) API is used to replace resources. In this way, **js/script.js** can be successfully loaded.
18
19  ```ts
20  // main/ets/pages/Index.ets
21  import { webview } from '@kit.ArkWeb';
22
23  @Entry
24  @Component
25  struct Index {
26    @State message: string = 'Hello World';
27    webviewController: webview.WebviewController = new webview.WebviewController();
28    // Construct a mapping table between domain names and local files.
29    schemeMap = new Map([
30      ["https://www.example.com/index.html", "index.html"],
31      ["https://www.example.com/js/script.js", "js/script.js"],
32    ])
33    // Construct the local file and construct the return value format mimeType.
34    mimeTypeMap = new Map([
35      ["index.html", 'text/html'],
36      ["js/script.js", "text/javascript"]
37    ])
38
39    build() {
40      Row() {
41        Column() {
42          // For the local index.html file, use HTTP or HTTPS in place of file or resource as the protocol and construct a custom domain name.
43          // In this example, www.example.com is constructed.
44          Web({ src: "https://www.example.com/index.html", controller: this.webviewController })
45            .javaScriptAccess(true)
46            .fileAccess(true)
47            .domStorageAccess(true)
48            .geolocationAccess(true)
49            .width("100%")
50            .height("100%")
51            .onInterceptRequest((event) => {
52              if (!event) {
53                return;
54              }
55              // Search for the local offline resource to be loaded, and then intercept and replace the resource.
56              if (this.schemeMap.has(event.request.getRequestUrl())) {
57                let rawfileName: string = this.schemeMap.get(event.request.getRequestUrl())!;
58                let mimeType = this.mimeTypeMap.get(rawfileName);
59                if (typeof mimeType === 'string') {
60                  let response = new WebResourceResponse();
61                  // Construct the response data. If the local file is in rawfile, you can set the response data as follows:
62                  response.setResponseData($rawfile(rawfileName));
63                  response.setResponseEncoding('utf-8');
64                  response.setResponseMimeType(mimeType);
65                  response.setResponseCode(200);
66                  response.setReasonMessage('OK');
67                  response.setResponseIsReady(true);
68                  return response;
69                }
70              }
71              return null;
72            })
73        }
74        .width('100%')
75      }
76      .height('100%')
77    }
78  }
79  ```
80
81  ```html
82  <!-- main/resources/rawfile/index.html -->
83  <html>
84  <head>
85  	<meta name="viewport" content="width=device-width,initial-scale=1">
86  </head>
87  <body>
88  <script crossorigin src="./js/script.js"></script>
89  </body>
90  </html>
91  ```
92
93  ```js
94  // main/resources/rawfile/js/script.js
95  const body = document.body;
96  const element = document.createElement('div');
97  element.textContent = 'success';
98  body.appendChild(element);
99  ```
100
101- Method 2
102
103  Use [setPathAllowingUniversalAccess](../reference/apis-arkweb/js-apis-webview.md#setpathallowinguniversalaccess12) to set a path list for cross-origin access to local files using the file protocol. Note that only the resources in the path list can be accessed by the file protocol when this method is used. In this case, the behavior of [fileAccess](../reference/apis-arkweb/ts-basic-components-web.md#fileaccess) is overwritten. The paths in the list must be any of the following directories:
104
105  1. The application file directory and its subdirectories, which can be obtained through [Context.filesDir](../reference/apis-ability-kit/js-apis-inner-application-context.md#context), such as:
106
107  * /data/storage/el2/base/files/example
108  * /data/storage/el2/base/haps/entry/files/example
109
110  2. The application resource directory and its subdirectories, which can be obtained through [Context.resourceDir](../reference/apis-ability-kit/js-apis-inner-application-context.md#context), such as:
111
112  * /data/storage/el1/bundle/entry/resource/resfile
113  * /data/storage/el1/bundle/entry/resource/resfile/example
114
115  If a path is not any of the preceding paths, an error code 401 is reported and the path list fails to be set. When the path list is empty, the accessible files for the file protocol are subject to the behavior of [fileAccess](../reference/apis-arkweb/ts-basic-components-web.md#fileaccess). The following is an example:
116
117  ```ts
118  // main/ets/pages/Index.ets
119  import { webview } from '@kit.ArkWeb';
120  import { BusinessError } from '@kit.BasicServicesKit';
121
122  @Entry
123  @Component
124  struct WebComponent {
125    controller: WebviewController = new webview.WebviewController();
126
127    build() {
128      Row() {
129        Web({ src: "", controller: this.controller })
130          .onControllerAttached(() => {
131            try {
132              // Set the list of paths that allow cross-domain access.
133              this.controller.setPathAllowingUniversalAccess([
134                getContext().resourceDir,
135                getContext().filesDir + "/example"
136              ])
137              this.controller.loadUrl("file://" + getContext().resourceDir + "/index.html")
138            } catch (error) {
139              console.error(`ErrorCode: ${(error as BusinessError).code},  Message: ${(error as   BusinessError).message}`);
140            }
141          })
142          .javaScriptAccess(true)
143          .fileAccess(true)
144          .domStorageAccess(true)
145      }
146    }
147  }
148  ```
149
150  ```html
151  <!-- main/resource/rawfile/index.html -->
152  <!DOCTYPE html>
153  <html lang="en">
154
155  <head>
156      <meta charset="utf-8">
157      <title>Demo</title>
158      <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no,   viewport-fit=cover">
159      <script>
160  		function getFile() {
161  			var file = "file:///data/storage/el1/bundle/entry/resources/resfile/js/script.js";
162        // Use the file protocol to access the local JS file through XMLHttpRequest.
163  			var xmlHttpReq = new XMLHttpRequest();
164  			xmlHttpReq.onreadystatechange = function(){
165  			    console.log("readyState:" + xmlHttpReq.readyState);
166  			    console.log("status:" + xmlHttpReq.status);
167  				if(xmlHttpReq.readyState == 4){
168  				    if (xmlHttpReq.status == 200) {
169                  // If the path list is set on eTS, resources can be obtained.
170  				        const element = document.getElementById('text');
171                          element.textContent = "load " + file + " success";
172  				    } else {
173                  // If the path list is not set on eTS, a CORS error is triggered.
174  				        const element = document.getElementById('text');
175                          element.textContent = "load " + file + " failed";
176  				    }
177  				}
178  			}
179  			xmlHttpReq.open("GET", file);
180  			xmlHttpReq.send(null);
181  		}
182      </script>
183  </head>
184
185  <body>
186  <div class="page">
187      <button id="example" onclick="getFile()">stealFile</button>
188  </div>
189  <div id="text"></div>
190  </body>
191
192  </html>
193  ```
194
195  ```javascript
196  // main/resources/rawfile/js/script.js
197  const body = document.body;
198  const element = document.createElement('div');
199  element.textContent = 'success';
200  body.appendChild(element);
201  ```
202