来源https://www.cnblogs.com/yinghuochongfighter/p/5998194.html
一、首先我们要明白跨域的字面概念,读过留过印象之后,下面将会有例子进一步解释
有一篇文章《跨域的理解与实现》描述得很清楚,在这里摘录如下: 域(Domain)是Windows网络中独立运行的单位,域之间相互访问则需要建立信任关系(即Trust Relation)。信任关系是连接在域与域之间的桥梁。当一个域与其他域建立了信任关系后,2个域之间不但可以按需要相互进行管理,还可以跨网分配文件和打印机等设备资源,使不同的域之间实现网络资源的共享与管理。
简单的说就是A网站的javascript代码试图访问B网站,包括提交内容和获取内容,但由于安全原因,跨域访问是被各大浏览器所默认禁止的。
但怎么样才算跨域呢?这里举了几个例子(什么是跨域,什么是不算跨域)
http://www.a.com/a.js与http://www.a.com/b.js同一域名下允许
http://www.a.com/lab/a.js与http://www.a.com/script/b.js同一域名下不同文件夹允许
http://www.a.com:8000/a.js与http://www.a.com/b.js同一域名,不同端口不允许
http://www.a.com/a.js与https://www.a.com/b.js同一域名,不同协议不允许
http://www.a.com/a.js与http://70.32.92.74/b.js域名和域名对应ip不允许
http://www.a.com/a.js与http://script.a.com/b.js主域相同,子域不同不允许
http://www.a.com/a.js与http://a.com/b.js同一域名,不同二级域名(同上)不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js与http://www.a.com/b.js不同域名不允许
二、明白了跨域是什么之后,下面一一举出解决跨域的各种办法。
总的来说共有2种方法,
1.jsonp方式(其中jsonp方式可以用原生js,ajax,angular.js都可以实现)
实现之前,先理解下jsonp是什么?
为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。具体理解看代码:
1
2
3
4
5
|
<script>
function getData(){
var
script = document.createElement(
'script'
);
script.type =
'text/javascript'
;
// 同源策略不阻止将动态脚本元素插入文档中所以脚本元素是可以跨域访问的,所以这里用到原生js,其中callbackFunc其实是个方法,所以我们写回调方法名字要跟这个一致
|
1
2
3
4
5
6
7
8
|
var
myUrl =
'http://c.m.163.com/nc/article/headline/T1348647853363/0-10.html'
;
script.src =
'http://localhost:8000/?myUrl='
+ myUrl +
'&callback=callbackFunc'
;
document.getElementsByTagName(
'head'
)[0].appendChild(script);
}
function callbackFunc(data){
console.log(data);
}
</script>
|
下面看服务端的配合,上面代码中说了callback后跟的是一个函数是和后台约定好的函数,那么后台需要把jsonp数据包裹在整个函数里,然后返回处理后的数据,这样就完成了前端和后台的jsonp协议,实现了跨域请求
上面代码中有提到同源策略,这里补充一下。同源策略就是阻止从一个域上加载的脚本获取或操作另一个域上的文档属性(不阻止将动态脚本元素插入文档中所以脚本元素是可以跨域访问的)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var
http = require(
'http'
);
var
url = require(
'url'
);
var
qs = require(
'querystring'
);
http.createServer(function(req,res){
//创建一个结果对象,用来装载数据
var
resultData =
''
;
//取出网址后面的参数
var
query = url.parse(req.url).query;
console.log(query);
//先将字符串转化为对象
var
qs_parse = qs.parse(query);
console.log(qs_parse);
//再将muUrl参数当做路径发送请求
http.
get
(qs_parse.myUrl,function(request){
request.setEncoding(
'utf-8'
);
request.
on
(
'data'
,function(result){
resultData += result;
});
request.
on
(
'end'
,function(){
console.log(resultData);
|
1
2
3
4
5
6
7
8
|
var
str = qs_parse.callback +
'('
+ JSON.stringify(resultData) +
')'
;
res.end(str);
});
}).
on
(
'error'
,function(error){
console.log(error);
});
}).listen(8000);
console.log(
'server is running at 8000'
);
|
看ajax实现代码(服务器代码同上):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<script>
function getData(){
var
myUrl =
'http://c.m.163.com/nc/article/headline/T1348647853363/0-10.html'
;
var
url =
'http://localhost:8000/?myUrl='
+ myUrl;
//ajax会给我们生成那个callback
$.ajax({
/***
* 请求路径
* 请求方法
* 传输协议
* 成功、失败回调函数
*/
url:url,
type:
'get'
,
dataType:
'jsonp'
,
success:function(result){
console.log(JSON.parse(result));
},
error:function(e){
console.log(e);
}
});
}
</script>
|
看angular.js实现方法(服务器同上代码同上):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
angular.module(
'myApp'
,[])
.controller(
'myController'
,[
'$scope'
,
'$http'
,function($scope,$http){
$scope.getData = function(){
var
myUrl =
'http://c.m.163.com/nc/article/headline/T1348647853363/0-10.html'
;
$http({
method:
'jsonp'
,
url:
'http://localhost:8000/?myUrl='
+ myUrl +
'&callback=JSON_CALLBACK'
//这里的callback是angular自动生成的,JSON_CALLBACK代表着自动生成的回调函数名
}).then( function success(result){
console.log(result);
},function error(e){
console.log(e);
});
}
}]);
|
2.post设置请求头的方式,angularjs内置封装了类ajax的网络服务$http,从而实现了不依赖外部插件来完成完整的前后端分离方案。
angular.module('myApp',[]).controller('myController',['$scope','$http',function($scope,$http){ $scope.getData = function(){ $http({ method:'POST', headers:{ //在请求的时候设置请求头,当然请求有很多种,我们以这种为例 'content-type':'application/x-www-form-urlencoded' }, url:'http://localhost:8000', data:{ //字符串或者对象,这个对象包含了请求体的所有内容,会被发送给服务器 myUrl:'http://c.m.163.com/nc/article/headline/T1348647853363/0-10.html' } }).then(function success(result){ console.log(result); },function error(error){ console.log(error); }); } }]);
因为是post请求方式,服务器端代码也需要改动:(设置为*代表任意,这样前端和后台配合就完成了post请求的跨域操作、)
var http = require('http'); var url = require('url'); http.createServer(function(req,res){ //设置编码格式 req.setEncoding('utf-8'); //设置响应头,表示任意匹配,
res.setHeader('Access-Control-Allow-Origin','*'); var postData = ''; req.addListener('data',function(chunk){ postData += chunk; }); req.on('end',function(){ console.log('接受成功'); var myUrl = JSON.parse(postData).myUrl; console.log(myUrl); var result = ''; http.get(myUrl,function(request){ request.setEncoding('utf-8'); request.on('data',function(chunk){ result += chunk; }); request.on('end',function(){ res.end(result); }) }).on('error',function(error){ console.log(error); }); }); }).listen(8000);