关于前端js跨域的一些心得
原因
浏览器使用 XMLHttpRequest
发送请求的时候若是请求域名地址与当前域名不相同的时候,浏览器处于安全考虑会阻止请求的发送。这就是所谓的跨域问题。
解决方案
1. jsonp实现。
jsonp原理是浏览器请求资源文件的时候并不会去限制,所以可以请求到其他域的文件。下面是一个简单的实现。 jsonp.js
let connect = require("../connect");
let handleSendData = require("./ajaxData").setUrlencodedData;
let process = new connect();
// 默认参数
const defaultConfig = {
jsonp: 'callback',
prefix: "my_jsonp_callback_",
data: {},
success: function(result) {
console.dir(result);
},
headers: {
charset: "utf-8",
type: "text/javascript"
},
url: "",
fail: function() {},
error: function(err) {
console.dir(err);
}
};
// 创建唯一的callback函数的编号
let uniqueNumber = 1;
// 清空操作
let clear = function(data) {
let js = data.js;
let req = data.req;
js.parentNode && js.parentNode.removeChild(js);
delete window[req.data[req.jsonp]];
};
// 发送请求前的配置
let sendProcess = {
init: function() {
// 初始化需要处理的部分
process.use(this.setSendData);
process.use(this.setHeader);
process.use(this.loadEvent);
process.use(this.errorEvent);
},
// 设置script属性
setHeader: function(next, data) {
let req = data.req;
let js = data.js;
let headers = req.headers;
Object.keys(headers).forEach((key) => {
js.setAttribute(key, headers[key]);
});
next();
},
// 设置返回事件
loadEvent: function(next, data) {
let req = data.req;
window[req.data[req.jsonp]] = function(cb) {
req.success(cb);
clear(data);
};
next();
},
// 设置失败事件
errorEvent: function(next, data) {
let req = data.req;
let js = data.js;
js.onerror = (e) => {
req.error(e);
clear(data);
};
next();
},
// 设置传值
setSendData: function(next, data) {
let req = data.req;
// 设置返回的毁掉函数 函数名每次唯一所以不存在缓存问题
req.data[req.jsonp] = req.prefix + (++uniqueNumber);
handleSendData(data);
next();
}
};
let sendJsonp = function(data) {
data = Object.assign({}, defaultConfig, data);
let js = document.createElement("script");
process.data.js = js;
process.data.req = data;
process.handle();
let connectSymbol = "?";
if (~data.url.indexOf("?")) {
connectSymbol = "&";
}
js.src = `${data.url}${connectSymbol}${data.sendData}`;
document.getElementsByTagName("head")[0].appendChild(js);
};
sendProcess.init();
module.exports = sendJsonp;
复制代码
下面是node作为服务器部分的处理jsonp请求,简单的判断参数中是否有callback有的话将其作为函数名包裹返回数据返回。 response.js
const jsonp = {
jsonpKey: "callback",
isJsonp: function(req) {
if (req.body[this.jsonpKey]) {
return true;
}
return false;
},
handleJSONPsend: function(req, responseString) {
let callbackName = req.body[this.jsonpKey];
return `${callbackName}(${responseString})`;
}
};
exports = module.exports = function(req, res, next) {
res.sendJSON = function(data) {
let response = {
code: 200,
data: data
};
let responseString = JSON.stringify(response);
if (jsonp.isJsonp(req)) {
responseString = jsonp.handleJSONPsend(req, responseString);
}
res.send(responseString);
};
next();
};
复制代码
2 通过设置Access-Control-Allow-Origin
对于客户端,我们还是正常使用xhr对象发送ajax请求。
唯一需要注意的是,我们需要设置我们的xhr属性withCredentials为true,不然的话,cookie是带不过去的哦,设置: xhr.withCredentials = true;
对于服务器端,需要在 response header中设置如下两个字段:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials:true 这样,我们就可以跨域请求接口了。
XHR对象对于HTTP跨域请求有三种:简单请求
、Preflighted 请求
、Preflighted 认证请求
。简单请求
不需要发送 OPTIONS
嗅探请求,但只能按发送简单的GET、HEAD或POST请求,且不能自定义HTTP Headers
。Preflighted 请求
和认证请求
,XHR会首先发送一个 OPTIONS
嗅探请求,然后XHR会根据OPTIONS
请求返回的Access-Control-*
等头信息判断是否有对指定站点的访问权限,并最终决定是否发送实际请求信息。所以我们在network里面查看的时候会发现请求发了两次。
下面是《图解http》中对options的解释
options : 询问支持的方法,用来查询针对请求url指定资源支持的方法。