通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略,默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源,这种安全策略可以预防某些恶意行为。
CORS(Cross-Origin Resource Sharing) -- 跨域资源共享,W3C的工作草案,定义了在必须访问跨域资源时,浏览器与服务器该如何进行沟通,CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或者相应是成功还是失败。
在某些条件下,请求后台需要在headers中添加Token用以鉴权,这个时候需要CORS来帮忙,这里使用Node.js的Express来模拟后台配置响应头,使得将Token放入headers变为可行:
首先新建文件夹,并安装express:
npm install express
然后新建入口index.js、测试请求index.html
index.js:
const express = require('express');
const app = express()
app.listen(3300,()=>console.log('启动服务'));
app.post('/post',(req,res)=>{
let resObj = {
msg:"CORS",
headers:req.headers
}
res.json(resObj )
})
简单启动了一个端口为3300的服务,定义post请求一半公开侧事故
index.html:
//引用于JavaScript高级程序设计的兼容性xhr写法
function createXHR() {
if (typeof XMLHttpRequest != 'undefined') {
return new XMLHttpRequest();
} else if (typeof ActiveXObject != 'undefined') {
if (typeof arguments.callee.activeXString != 'string') {
var versions = [
'MSXML2 .XMLHttp.6.0', 'MSXML2.XMLHttp.3.0',
'MSXML2.XMLHttp'
],
i, len;
for (i = 0, len = versions.length; i < len; i++) {
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex) {
// pass;
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error('no XHR Object')
}
}
}
//简单封装ajax 传入url,headers,data
function ajax(url, headers, data) {
return new Promise((resolve, reject) => {
var xhr = createXHR();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if ((xhr.readyState >= 200 && xhr.status < 300) || xhr.status == 304) {
resolve(xhr.responseText);
} else {
reject(xhr.status)
console.log('Request was unsuccessful: ' + xhr.status)
}
}
};
xhr.open('post', url, true);
if (headers) {
headers.forEach(element => {
let {
name,
value
} = element;
xhr.setRequestHeader(name, value);
});
}
xhr.send(data)
})
}
//测试用 随便写的
var data = new FormData();
data.append('name', 'Nicholas');
let url = 'http://127.0.0.1:3300/post'
// 设置头 将Token带入
let headers = [
{
name: "Token",
value: "XXX3RT-AASDR-FFDSX-RRESA"
}
]
//调用
ajax(url, headers, data).then(res => {
console.log(res)
}).catch(e => {
console.log(e)
})
这里简单写了一个Ajax并将token放入headers,这个时候如果向测试口发请求:
首先会报跨域错误:未设置Access-Control-Allow-Origin头,现在在index.js中把头加上:
const express = require('express');
const app = express()
app.listen(3300,()=>console.log('启动服务'));
//express中间件
app.use("*",(req,res,next)=>{
// 允许任意源
res.header('Access-Control-Allow-Origin',"*");
next();
})
app.post('/post',(req,res)=>{
let resObj = {
msg:"CORS",
headers:req.headers
}
res.json(resObj )
})
这个时候再请求:
会出现另一个跨域错误:不允许我们更改请求头,这是因为:
跨域的XHR存在一些限制:
CORS通过这个透明服务验证机制支持开发人员用自定义头部、GET,POST以外的方法,以及不同类型的主体内容,在使用这些类项来发送请求时,就会向服务器发送一个Preflight请求,这种请求使用OPTIONS方法。
这就需要我们在index.js中通过响应与浏览器进行沟通:
const express = require('express');
const app = express()
app.listen(3300,()=>console.log('启动服务'));
// 中间件
app.use("*",(req,res,next)=>{
// 允许任意源
res.header('Access-Control-Allow-Origin',"*");
// 允许的请求方法 修改请求头时会发送预检请求
res.header('Access-Control-Allow-Methods','GET,POST,OPTIONS')
// 允许自定义头部
res.header('Access-Control-Allow-Headers',"Token");
// 收到遇见请求返回成功状态
if(req.method == 'OPTIONS'){
res.send(200)
}else{
next();
}
})
app.post('/post',(req,res)=>{
let resObj = {
msg:"CORS",
headers:req.headers
}
res.json(resObj )
})
将这些响应设置好以后我们再请求,首先看一下netWork:
发现发送了两次请求:
第一次为OPTIONS:
第二次为真正的POST请求:
请求成功,并且Token带入了headers,至此完成了模拟CORS头部带Token请求的写法.
注:如果在index.js的中间件中增加:
res.header('Access-Control-Max-Age',1728000)
Preflight请求结束以后,结果将按照响应中指定的时间缓存起来,再次请求将不会在进行Preflight请求。
参考:JavaScript高级程序设计,https://www.jianshu.com/p/f650dfad5574