了解 webview 请求拦截防止 cors 跨域
下午有伙伴已经通过访问本地资源文件,打开 web 项目了。
但是也提到,本地跨域,那么我们看一下如何解决。首先了解一下 CoRS。
跨源资源共享
跨源资源共享(CORS[1],或通俗地译为跨域资源共享)是一种基于 HTTP[2] 头的机制,该机制通过允许服务器标示除了它自己以外的其他源[3](域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
跨源 HTTP 请求的一个例子:运行在 https://domain-a.com
的 JavaScript 代码使用 `XMLHttpRequest`[4] 来发起一个到 https://domain-b.com/data.json
的请求。
出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。例如,XMLHttpRequest
和 Fetch API[5] 遵循同源策略[6]。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。
也就是说只要我们定义了正确的响应头也是可以处理的,这里面鸿蒙 webview 组件提供就请求拦截事件。
onInterceptRequest
我们可以通过 onInterceptRequest 事件,拦截 http 请求中的每一个细节。从而返回我们需要的 Web 资源数据。
示例
代码语言:javascript复制// xxx.ets
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();
responseWeb: WebResourceResponse = new WebResourceResponse();
heads: Header[] = new Array();
@State webData: string = "<!DOCTYPE html>n"
"<html>n"
"<head>n"
"<title>intercept test</title>n"
"</head>n"
"<body>n"
"<h1>intercept test</h1>n"
"</body>n"
"</html>";
build() {
Column() {
Web({ src: 'www.example.com', controller: this.controller })
.onInterceptRequest((event) => {
if (event) {
console.log('url:' event.request.getRequestUrl());
}
let head1: Header = {
headerKey: "Connection",
headerValue: "keep-alive"
}
let head2: Header = {
headerKey: "Cache-Control",
headerValue: "no-cache"
}
let length = this.heads.push(head1);
length = this.heads.push(head2);
const promise: Promise<String> = new Promise((resolve: Function, reject: Function) => {
this.responseWeb.setResponseHeader(this.heads);
this.responseWeb.setResponseData(this.webData);
this.responseWeb.setResponseEncoding('utf-8');
this.responseWeb.setResponseMimeType('text/html');
this.responseWeb.setResponseCode(200);
this.responseWeb.setReasonMessage('OK');
resolve("success");
})
promise.then(() => {
console.log("prepare response ready");
this.responseWeb.setResponseIsReady(true);
})
this.responseWeb.setResponseIsReady(false);
return this.responseWeb;
})
}
}
}
并且在这里我们还可以伪造几乎所有 HTTP 所需要的 Response 头信息。
返回我们 DevEco 编辑器,仔细查看。
关于 setResponseData 这个关键 api 的参数,我们不仅可以放入字符串。我们还可以放入 number | Resource | ArrayBuffer 类型。
setResponseData
setResponseData(data: string | number | Resource | ArrayBuffer): void
设置资源响应数据。
参数:
参数名 | 参数类型 | 必填 | 默认值 | 参数描述 |
---|---|---|---|---|
data | string | number | Resource[7]10 | ArrayBuffer11 | 是 | - | 要设置的资源响应数据。string 表示 HTML 格式的字符串。number 表示文件句柄, 此句柄由系统的 Web 组件负责关闭。Resource 表示应用 rawfile 目录下文件资源。ArrayBuffer 表示资源的原始二进制数据。 |
setResponseHeader
setResponseHeader(header: Array
)
设置资源响应头。
参数:
参数名 | 参数类型 | 必填 | 默认值 | 参数描述 |
---|---|---|---|---|
header | Array<Header[8]> | 是 | - | 要设置的资源响应头。 |
所以我们需要在整个 Web 组件启动请求的那一刻之前,就把需要的处理在这儿处理就可以了。关于其他的一些场景,大家也可以按照自己的需要,查看对应的接口,处理。
参考
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-web-V5#oninterceptrequest9
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-web-V5#oninterceptrequestevent12
参考资料
[1]
CORS: https://developer.mozilla.org/zh-CN/docs/Glossary/CORS
[2]
HTTP: https://developer.mozilla.org/zh-CN/docs/Glossary/HTTP
[3]
源: https://developer.mozilla.org/zh-CN/docs/Glossary/Origin
[4]
XMLHttpRequest
: https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
[5]
Fetch API: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API
[6]
同源策略: https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
[7]
Resource: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-types-V5
[8]
Header: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-basic-components-web-0000001477981205-V2#ZH-CN_TOPIC_0000001523968730__header