一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
为什么会有跨域
由于浏览器的同源策略,浏览器会拒绝跨域请求
同源
浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。所以,当域名、协议、端口都相同时,视为属于同域。同域之间相互请求资源,就是同源。
同源策略: 协议(http https) 端口 主机 任何一个不一样都会产生跨域
JSONP
大家都知道 HTML 中的 script 标签可以用来加载其他域线上的数据当做 JS 代码来执行 ,
提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。
换句话说,JSONP 需要对应接口的后端的配合才能实现。
看个例子
<script>
function json(a){
console.log(a);
}
</script>
<script src="http://a.com?callback=json"></script>
CORS
CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式。 当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:
Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。
简单请求和复杂请求
-
简单请求
请求方法:get post head
除了以下请求头没有自定义请求头:- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
Content-Type 的值只有以下三种(Content-Type 一般是指在 post 请求中,get 请求中设置没有实际意义)
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded 简单请求不会进行预请求
-
预请求
“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
服务器检查了预请求请求头确认是否允许跨域,预请求是不会携带请求参数
-
复杂请求
非简单请求就是复杂请求,加入了一些自定义的请求也是复杂请求 复杂请求每次请求都会先预请求,
-
Nginx 配置跨域
worker_processes 1;
events {
worker_connections 1024;
}
http {
... 省略代码
# 跨域
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
... 省略代码
}
postMessage
Window.postMessage API 的功能是允许程序员跨域在两个窗口/frames 间发送数据信息。基本上,它就像跨域的 ajax,但是它不是浏览器和服务器之间的交互,它是两个客户端之间的通信。
postMessage 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本文档、多窗口,跨域消息传递。
otherWindow.postMessage(message, targetOrigin, [transfer]);
实际前端处理跨域的常见比较少见,基本上是后端处理 根据 CORS 添加相应的源
开发环境配置跨域
在开发环境如果遇到跨域问题,并且服务端不帮我们解决,那么我们可以自己设置 一般我们的项目 使用 webpack 或者是基于 vue-cli 的脚手架项目,都是可以设置代理
基本原理是使用了 http-proxy-middleware
http 代理的中间件 ,之前我们的请求是直接发起服务器请求,现在我们设置了代理,相当于中转了一层,发起请求到 HTTP 代理,由中间层帮我们发起请求,(中间层发起请求会替换喂服务器的域名) 所以客户端和服务端就相当于在同一个域名下,不会由跨域问题
devServer: {
proxy: 'http://localhost:4000'
}
// 或者
devServer: {
proxy: {
'/api': {
target: '<url>',
ws: true,
changeOrigin: true
},
'/foo': {
target: '<other_url>'
}
}
}
这种方法只能用开发环境,正式环境的话需要使用 nginx