前言
以前写前端小项目的时候就听说过跨域这个词,什么 JSONP啊,CORS啊。感觉很高级,但是无奈项目太小没机会用上。今天就写篇博客总结一下常用的跨域操作。
为什么要跨域
一般来说写项目的时候都是自己给自己发请求的,如 localhost:8080 发了一个 /users 请求,本质上就是向 localhost:8080/users 发请求,这是被允许的。但是如果给支付宝发是不行的,如 post /zhifubao/sendMoney/user?money=1000
,如果允许的话那每个人装一个 postman 不断发请求就可以去支付宝偷钱了(不考虑验证情况下)。所以呢跨域请求是不好的,但是在某些条件下是可以的。像大家都是兄弟公司,虽然我们域名不一样但是我们有 py 交易呀,数据都共享,你发请求给我,我也发请求给你,这就需要跨域请求了。
JSONP
JSONP 其实真名叫:JSON + Padding。JSON 大家都知道跟 JS 对象差不多,是一种通信格式。至于 Padding ,emmm 如果你知道这个原因就感觉很 SB。
为什么会有 JSONP
JSONP 本来不是用来解决跨域问题的,是用来实现发请求时不重刷页面的,这都是 Ajax 出现前的一个技术,或者说是一个替代品,只不过刚好可以用来实现跨域请求。
前端实现
实现思路是
- 定义一个
loadData
函数在全局
function loadData(data) {
console.log(data)
}
- 创建一个
标签,并在
src
加上请求 url 如:/hello?callback=loadData
- 添加这个
标签到 document.body 上。一旦添加了浏览器马上会发一个
/hello?callback=loadData
的 GET 请求 - 等服务器响应后,就会执行返回的 JS 代码。一般来说这 JS 代码就是这个 loadData(data),这个 data 是服务器添加上去的,至于怎么返回 JS 代码请看服务端实现
后端实现
- 首先获取查询参数里的 callback(回调函数的名字,这里就是 loadData)
- 然后生成要返回的数据
- 将数据和前端提取出来的回调函数结合,生成 JS 代码,返回到浏览器,浏览器一收到就会执行
app.get('/hello', (req,res) => {
let callback = req.query.callback;
let obj = {
userName: 'Jack',
password: '123456'
};
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(callback + '(' + JSON.stringify(obj) + ')');
})
好了,这里说下 Padding 是啥,你看到 userName
和 password
前面的字符,那就是 Padding,没了。JSON 是因为传入回调函数的数据一般为 JSON 格式。
CORS
CORS 全名是 Corss Origin Resource Sharing。这种方法比较简单,因为前端发请求就好了,后端也正常返回数据。要改的是后端在返回响应时要添加一个响应头。
app.post('/hello',(req,res) => {
if(req.headers.origin){
res.writeHead(200, {
"Content-Type": "text/html; charset=UTF-8",
"Access-Control-Allow-Origin":'http://127.0.0.1:8888'
});
let user= {
userName: 'Jack',
password: '123456'
}
res.end(JSON.stringify(user));
}
})
这里解释一下:当浏览器发现发了跨域请求就会在请求头里添加 Origin: 当前域
字段。服务器此时会检测是否存在 Origin 字段,如果存在那么会在响应头里添加 Access-Control-Allow-Origin: Origin 的值
再返回 JSON 格式结果就行了。前端收到响应后,浏览器会检查 Access-Control-Allow-Origin
的值是否和当前的地址相同,如果相同才能获取到数据。
postMessage
这个方法主要是在前端完成的。在发送请求方只需要调用新 API 的 postMessage
函数就可以了。
postMessage('要发送的消息', '目的地地址', '当前地址')
在接受方要在 window
上监听一个 message
事件。
function receiveMessage(event) {
// 对发送者做检查 ,如果不是自己人就不返回
if (event.origin !== "http://example.com:8080")
return;
// 这里可以打印发送方的消息
console.log(event.data)
// 如果是自己人,就返回数据
event.source.postMessage("你好,自己人",event.origin)
}
// 全局监听 `message` 事件
window.addEventListener("message", receiveMessage, false);
这种方法比较简单,但是也要做好发送方的检测。