# 跨域
# Jsonp
- jsonp是一种跨域通信手段,通过script标签的src属性实现跨域,因为浏览器的同源策略不会截断script的跨域响应。
- 通过将前端方法名作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。
- 由于使用script标签的src属性,因此只支持get方法。
// 定义回调函数
function fn(arg) {
// arg为服务端传来的数据
console.log(`客户端获取数据: ${arg}`);
}
// 创建script标签
const s = document.createElement('script');
// 给script标签的src属性赋值,值为请求url,查询参数callback,需与后端对应
// fn为前端回调函数名
s.src = 'http://host:7001/test?callback=fn';
// 向html添加此标签,添加完成之后浏览器自动请求script的src对应的网址
document.getElementsByTagName('head')[0].appendChild(s);
// 等待浏览器收到响应之后,会自动执行响应内容的代码
----------------------
// 后端
// 后端获取url中的callback就是函数名
fn('test');
总流程就是,前端先注册一个函数,例如test,然后在url告诉后端,函数名是啥。
后端返回时,获取callback函数名,并执行函数,例如test('我是传给前端的内容')。
因为JS文件,返回前端时,就会执行JS文件的代码,进而调用。
# CORS
- 跨域资源共享cors,它使用额外的HTTP头来告诉浏览器,让运行在一个origin(domain)上的Web应用被准许访问来自不同源服务器上的指定的资源。
- 需要服务器与客户端同时支持cors跨域方式才能进行跨域请求,服务端通过设置
Access-Control-Allow-Origin: *
,即可开启cors允许跨域请求,使用通配符*表示允许所有不同域的源访问资源,也可单独设置指定允许的源域名。 - 使用cors跨域时,将会在发起请求时出现2种情况:
- 简单请求,需满足以下条件:
- 使用get、head、post方式发起的请求。
- Content-Type的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 不满足这些条件即为预检请求
- 预检请求
- 需预检的请求要求必须首先使用OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。
- 预检请求的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
- 当满足以下条件之一,将会发送预检请求:
- 使用了下面任一HTTP方法:
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
- 人为设置了对CORS安全的首部字段集合之外的其他首部字段。该集合为:
- Accept
- Accept-Language
- Content-Language
- Content-Type(需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type的值不属于下列之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 使用了下面任一HTTP方法:
- 简单请求,需满足以下条件:
满足以上条件之一将会发起预检请求,总共会发起2次请求,第一次为OPTIONS方式的请求,用来确定服务器是否支持跨域,如果支持,再发起第二次实际请求,否则不发送第二次请求。
# postMessage
- postMessage可用于不同页面之间的跨域传递数据。
postMessage(data, origin[, source])
,data为发送的数据只能发送字符串信息,origin发送目标源,指定哪些窗口能接受到消息事件,如果origin设置为*则表示无限制,source为发送消息窗口的window对象引用。
<!-- test.html -->
<iframe
src="http://127.0.0.1:5501/postMessage.html"
name="postIframe"
onload="messageLoad()">
</iframe>
<script>
// 定义加载之后执行的函数,给postMessage.html发送数据
function messageLoad() {
const url = 'http://127.0.0.1:5501/postMessage.html';
window.postIframe.postMessage('给postMessage的数据', url);
}
// 用于监听postMessage.html的回馈,执行回调
window.addEventListener('message', event => {
console.log(event.data);
});
</script>
----------------------------------
<!-- postMessage.html -->
<script>
// 监听test.html发来的数据,延迟1秒返回数据
window.addEventLister('message', event => {
setTimeout(() => {
event.source.postMessage('给test的数据', event.origin);
}, 1000);
});
</script>
- event对象的几个重要属性
- data:从其他窗口发送过来的消息对象
- type:发送消息的类型
- source:发送消息的窗口对象
- origin:发送消息的窗口的源
# window.name
- 由于
window.name
属于全局属性,在html中的iframe加载新页面(可以是跨域),通过iframe设置的src指向的源中更改name的值,同时主页面中的name也随之更改,但是需要给iframe中的window设置为about: blank
或者同源页面即可。 - iframe使用之后应该删除,name的值只能为string类型,且数据量最大支持2MB。
<!-- test.html -->
<script>
// 封装应该用于获取数据的函数
function foo(url, func) {
let isFirst = true;
const ifr = document.createElement('iframe');
loadFunc = () => {
if (ifFirst) {
// 设置为同源
ifr.contentWindow.location = 'about: black';
isFirst = false;
} else {
func(ifr.contentWindow.name);
ifr.contentWindow.close();
document.body.removeChild(ifr);
}
};
ifr.src = url;
ifr.style.display = 'none';
document.body.appendChild(ifr);
// 加载之后的回调
ifr.onload = loadFunc;
}
foo(`http://127.0.0.1:5501/name.html`, data => {
console.log(data);
});
</script>
-------------------------------------------------
<!-- name.html -->
const obj = {name: "iframe"};
// 修改name的值,必须为string类型
window.name = JSON.stringify(obj);
# document.domain
document.domain
的值对应当前页面的域名。- 通过对domain设置当前域名来实现跨域,不过仅限于域名不同,但是又要属于同一个基础域名下,如
http://a.baidu.com
与http://b.baidu.com
这两个子域名之间才能使用domain跨域,一般用于子域名之间的跨域访问。 - domain只能赋值为当前域名或者其基础域名,即上级域名。
<!-- test.html -->
<script>
document.domain = 'baidu.com';
const ifr = document.createElement('iframe');
ifr.src = 'a.baidu.com/test.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function() {
const doc = ifr.contentDocument || ifr.contentWindow.document;
// 操作domain.html的document
ifr.onload = null;
};
</script>
--------------------------------------------------
<!-- domain.html -->
<script>
// domain.html下设置与test.html中的domain一致
document.domain = 'baidu.com';
</script>
通过设置为同源域名(只能为其基础域名),通过iframe操作另一个页面的内容。
# nginx反向代理
- nginx反向代理,代理从客户端来的请求,转发到其代理源。
- 配置nginx的配置文件实现代理到不同源。
// nginx.conf配置
server {
listen 80; // 监听端口
server_name www.baidu.com; // 匹配来源
location / { // 匹配路径
// 反向代理到http://127.0.0.1:3000
proxy_pass http://127.0.0.1:3000
// 默认入口文件
index index.html index.hml index.jsp;
}
nginx反向代理还能实现负载均衡。
← 浏览器存储 async与defer →