说到跨域请求, 一个绕不开的话题就是同源策略;(同协议+同域名+同端口)视为同源。它是浏览器的一个安全功能,保护用户数据(Cookie、 Storage、 indexDB)被恶意获取; 阻止浏览器的脚本去请求非同源下的资源;阻止获取DOM。下面说的主要是跨域请求的常用解决办法。
JSONP
浏览器中html标签script可以通过src属性从不同域名下加载资源,不受同源策略的影响。可以基于此解决跨域请求; 通过动态创建script标签, 设置script的src属性,然后通过document.head.append将其挂载到页面。当页面执行到这<script>这行代码的时候, 会向服务端去加载src所指向的资源,服务器收到请求后会将数据放入指定的回调函数中传回,资源请求成功后浏览器会自动执行js代码。
//客户端代码
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://localhost:8888/api/user?callback='+handleCallback;
document.head.appendChild(script);
function handleCallback(res) {
alert(JSON.stringify(res));
}
//服务端代码
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
const response = {code: 0, data: {name: "express"}}
//注意这里需要是一个自执行函数,才能在返回的时候,自动执行这个函数
var str = `(${req.query.callback})(${JSON.stringify(response)})`;
res.end(str)
})
app.listen(8888)
服务器端设置代理
跨域只是出现在浏览器与服务器,并不会出现在服务器与服务器的通信中。通常我们的前端文件与后端的接口不在同一个服务中, 我们可以在前端的服务器中通过代理的方式,将本来在浏览器中发起的请求转发到代理服务器,由代理服务器向接口服务器发起请求,再由代理服务器将接口响应返回到浏览器。
CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器发起XMLHttpRequest请求。
//客户端 使用ajax模拟跨域调用
$.get("http://localhost:8888/api/user", res => {
console.log(res)
})
//模拟服务端接口
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
res.json({code: 0, data: {name: "express"}})
})
app.listen(8888)
如果可以在服务端设置响应头 Access-Control-Allow-Origin为* 就可以在客户端获取到数据
//模拟服务端接口
var express = require('express');
var app = express()
app.use("/api/user", (req, res) => {
res.header(" Access-Control-Allow-Origin", "*") //允许请求
res.json({code: 0, data: {name: "express"}})
})
app.listen(8888)
浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求(options请求)
浏览器将CORS的跨域请求分为两类一类简单请求, 一类非简单请求
只有同时满足以下两条件才能算是简单请求
1.请求方式必须是GET、POST、HEAD三者之一
2.请求头字段信息是以下几种
-
Accept
-
Accept-Language
-
Content-Language
-
Last-Event-ID
-
Content-Type(他的值仅限于三个
application/x-www-form-urlencoded,
multipart/form-data
text/plain
)
当浏览器发现这是一个简单的跨域请求,会自动在请求头中添加一个origin属性,origin头部字端用于说明本次请求的源(协议+域名+端口)。服务器就是依据此字端来跟Access-Control-Origin-Allow的值比较。决定是否响应该请求。如果origin指定的源不在服务器许可范围内,XMLHttpRequest的onerror就会监听到错误,此时不能根据状态码判断
如果origin指定的源在服务器许可范围内,服务器返回的响应会多出几个响应头字段
Access-Control-Allow-Origin: 这个字段是必须的,要么是请求头中的origin值,要么就是*
Access-Control-Allow-Credentials: 这是一个可选字段,表示服务器许可cookie可以包含在请求头中发送, 但是还需要浏览器也同时设置
WithCredentials为true, 浏览器默认不携带如果服务器想禁止浏览器发送cookie, 直接删除该字端就好。**Access-Control-Expose-Headers: **这也是一个可选字段, 通常我们通过response.header只能获取到Expires、Cache-Control、Last-Modified、Content-Type, Content-Language、Pragma这6个字段,如果想要获取其他字段需要在此指定才能获取的到。
对于非简单请求, 例如使用了自定义的header字段, 或者Content-Type设置为application/json等, 浏览器都会自动先发送一个OPTIONS的预检请求, 询问服务器是否可以这样请求(当前网页的域名是否在服务器的许可名单中、是否有自定义的请求头字端、请求方法是否是post、get、head三者之一, content-type值是否符合类型)。预检请求会在请求头中自动添加两个请求头字段
Access-Control-Request-Method 指明请求方法
Access-Control-Request-Headers 该值是一个逗号分隔的字符串, 那些自定义字段
只有在得到肯定的答复后,浏览器才会发起正式的请求。