跨域
跨域
定义
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源
同源策略
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源.浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
跨域的实现形式
1 通过jsonp跨域
2 document.domain + iframe跨域
3 location.hash + iframe
4 window.name + iframe跨域
5 postMessage跨域
6 跨域资源共享(CORS)
7 nginx代理跨域
8 nodejs中间件代理跨域
9 WebSocket协议跨域
JSONP
HTML 中 script 标签可以加载其他域下的js,比如我们经常引入一个其他域下线上cdn的jQuery。那如何利用这个特性实现从其他域下获取数据?
如果这样写:
<script src="http://api.jirengu.com/weather.php"></script>
这时会向天气接口发送请求获取数据,获取数据后做为 js 来执行。 但这里有个问题, 数据是 JSON 格式的数据,若直接作为 JS 来运行,那可以这样写:
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>
这个请求到达后端后,后端会去解析callback这个参数获取到字符串showData,在发送数据做如下处理:
之前后端返回数据: {"city": "hangzhou", "weather": "晴天"} 现在后端返回数据: showData({"city": "hangzhou", "weather": "晴天"}) 前端script标签在加载数据后会把 「showData({“city”: “hangzhou”, “weather”: “晴天”})」作为 js 来执行,这实际上就是调用showData这个函数,同时参数是 {“city”: “hangzhou”, “weather”: “晴天”}。 用户只需要在加载提前在页面定义好showData这个全局函数,在函数内部处理参数即可。
function showData(ret){
console.log(ret);
}
将此函数加入到JS中即可。
总结
JSONP是通过 script 标签加载数据的方式去获取数据当做 JS 代码来执行,提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP 需要对应接口的后端的配合才能实现。
例子
index.html
<!DOCTYPE html>
<html>
<body>
<div class="container">
<ul class="news">
</ul>
<button class="show">show news</button>
</div>
<script>
$('.show').addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
document.head.appendChild(script);
document.head.removeChild(script);
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id){
return document.querySelector(id);
}
</script>
</html>
server.js
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')
http.createServer(function(req, res){
var pathObj = url.parse(req.url, true)
switch (pathObj.pathname) {
case '/getNews':
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心"
]
res.setHeader('Content-Type','text/json; charset=utf-8')
if(pathObj.query.callback){
res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')
}else{
res.end(JSON.stringify(news))
}
break;
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
if(e){
res.writeHead(404, 'not found')
res.end('<h1>404 Not Found</h1>')
}else{
res.end(data)
}
})
}
}).listen(8080)
终端调试
image image