以前开发的时候在本地请求接口总是会遇到跨域问题,当时基础也不扎实,也不懂的怎么解决,现在趁机会总结一下跨域的概念和几种方法。
# 同源策略
要知道什么是跨域,首先得了解一下浏览器的同源策略
为了保证用户信息的安全,防止恶意网站窃取数据,所以有了同源策略。
# 什么情况算是同源?
- 协议相同
- 域名相同
- 端口相同
https://zyczxq.com:443 |
对于这个网站来说,它的协议是 https://,域名是:zyczxq.com,端口是 443
如果协议、域名、端口有任意一个不相同,都算是非同源。
# 非同源不能干哪些事?
- Cookie、LocalStorage、IndexDB 无法读取
- 无法获取 DOM
- 不能发送 AJAX 请求
# 跨域的办法
跨域的方法非常多,这里只说几种我平时用到过的几种
# JSONP
JSONP,全称为 JSON with padding,它的做法是通过动态添加一个脚本,因为页面调用 JS 文件不受同源策略限制,所以可以通过 script 标签进行跨域请求。
- 网页动态插入一个 script 元素,由它向目标发出请求,同时这个请求需要设置好回调函数,作为 url 的参数
- 服务器收到请求后,通过该参数获取到回调函数的名字,并将数据放在回调函数的参数中返回。
- 收到结果后因为是 script 标签,所以浏览器会执行
// 假设我们有一个回调函数 foo,需要数据 ip | |
function foo(ip); | |
// 添加一个 script 标签,设置 src 为接口地址 + callback=foo |
然后服务器返回的内容写:
foo({ip:'....'}); |
很明显,JSONP 只能是 GET 请求,无法支持比较复杂的 POST 请求和其他请求。
# CORS
CORS 是一个 W3C 标准,全称为 Cross-origin resource sharing (跨域资源共享)。
它允许浏览器向跨源服务器发出 XMLHttpRequest 请求。
实现 CORS 通信的关键在于服务器。
在 CORS 之中分为两类请求:简单请求和非简单请求
# 什么算是简单请求?
请求方法是下述三种之一:
- HEAD
- GET
- POST
HTTP 的头信息不超过以下几种:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
# 简单请求
对于简单请求,浏览器直接发出 CORS 请求,就是在头部信息中增加一个 origin 字段,表示请求的 “源”(协议 + 端口 + 域名),服务器根据这个值,来决定是否同意本次请求。
如果服务器同意,在返回来的请求中,会多出几个头信息:
- Access-Control-Allow-Origin:表示允许的源,该字段是必须的。它的值要么是请求时
Origin
字段的值,要么是一个*
,表示接受任意域名的请求。 - Access-Control-Allow- Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。
- Access-Control-Expose-Headers
不同意的话,就看头信息有没有这几个字段就可以了
实际上不同意 HTTP 返回的状态码也有可能是 200
如果要发送 Cookie,开发者这边还必须指定 xmlHttpRequest 对象的 withcredentials 属性为 true。同时需要注意,如果要发送 cookie,Access-Control-Allow-Origin 的值就不能设置为 “*”
# 非简单请求
对于非简单请求,在正式通信之前,浏览器会发出一个预检请求,询问服务器是否允许该域等信息。
如果不同意,服务器会返回一个正常的回应,但不带有任何 CORS 相关字段。
# 优化非简单请求
优化 OPTIONS 预检请求的发送,CORS 中 Access-Control-Max-age 可以设置缓存的时间,表示多少秒内不会对同一个非简单请求去发送预检请求,这样的话就能够减少重复多次发送 options 请求的往返时间。
# 设置代理服务器进行转发
比如说 cli 工具中的代理
# webpack 中的 proxy
在 webpack 配置项中加入如下:
devServer: { | |
port: 8000, | |
proxy: { | |
"/api": { | |
target: "http://localhost:8080"// 服务器地址 | |
} | |
} | |
}, |
同时,在请求的时候不要带 url,现在已经是同源了
# vuecli2.x
在 vuecli 的配置文件中:
proxyTable: { | |
'/api': { | |
target: 'http://localhost:8080', | |
} | |
}, |
# vuecli3.x
我用的比较多的方式就是这种,在 vue.config.js 中添加
module.exports = { | |
devServer: { | |
port: 8000, | |
proxy: { | |
"/api": { | |
target: "http://localhost:8080", | |
changeOrigin:true, | |
pathReWrite:{ | |
'/api':"" | |
} | |
} | |
} | |
} | |
}; |
# 还有利用 iframe 进行跨域的
这个暂时用的还比较少,感觉查资料难以理解,先留个坑,等具体用到的时候再补