标签:
请求跨域问题的产生原因是浏览器的同源策略(Same origin policy),这是有Netscape提出的一个著名的安全策略,对于浏览器而言,它提供了最基本也是最核心的安全功能。它所指的同源是域名、协议、端口都相同。
从wiki百科上截了个例子说明:
从图中可以看出,三者只要任何一个不相同,都会导致Failure.
什么样的姿势跨域,才能成功的跨域?
贴上一份简单的node代码,用以说明服务端的情况:
var http = require(‘http‘);
var fs = require(‘fs‘);
var reg = /(^|\?|&)callback=\w*/;
var MINE_TYPE = {
‘css‘: ‘text/css‘,
‘html‘: ‘text/html‘,
‘js‘: ‘text/javascript‘,
‘txt‘:‘text/plain‘
};
http.createServer(function(req,res){
var _url = req.url;
var data = _url.indexOf(‘callback‘) >=0 ? req.url.match(reg)[0].substr(10)+‘("XMLHttpRequest is success")‘:‘XMLHttpRequest is success‘;
res.writeHead(200,{‘Content-Type‘:MINE_TYPE[‘txt‘],‘Access-Control-Allow-Origin‘:‘http://192.168.1.162:9988‘});
res.end(data);
}).listen(3003,‘192.168.1.162‘);
function _server(port){
http.createServer(function(req,res){
var pathname = req.url.substr(1);
var _ext = pathname.split(‘.‘).pop();
fs.readFile(pathname,‘utf-8‘,function(err,data){
res.writeHead(200,{‘Content-Type‘:MINE_TYPE[_ext]});
res.end(data)
})
}).listen(port,‘192.168.1.162‘);
}
_server(8899);
_server(9988);
先以普通的姿势来跨一次(本文调试使用端口条件处理同源):
html:
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.0/jquery.js"></script> <div class="xmlhttprequset">发送请求</div>
js:
$(‘.xmlhttprequset‘).click(function(){
$.get(‘http://192.168.1.162:3003‘,function(res){
console.log(res);
})
})
我们在浏览器url框输入192.168.1.162:8899/test.html和192.168.1.162:9988/test.html打开两个页面,分别点击页面上的"发送请求",得到以下结果:
XMLHttpRequest cannot load http://192.168.1.162:3003/. No ‘Access-Control-Allow-Origin‘ header is present on the requested resource. Origin ‘http://192.168.1.162:port‘ is therefore not allowed access.
看到这个错误,心里一如既往的爽...那么爽完之后,该解决错误了,最常见的是jsonp处理跨域问题:
js:
$(‘.xmlhttprequset‘).click(function(){
$.ajax({
type:‘get‘,
dataType:‘jsonp‘,
url:‘http://192.168.1.162:3003‘,
success:function(res){
console.log(res);
}
});
})
于是,我们就能够在控制台看到打印出来的东西了...
那么我不想使用jsonp呢,能否实现跨域呢?答案是肯定的。而且还可以允许9988端口跨域请求,对8899端口进行"丑拒",就是这么任性~ 办法就是在response头配置"Access-Control-Allow-Origin"参数的值来控制给不给跨,不给跨,给跨,跨... ←_← 这真的只是回声.
于是我们将server.js代码改了改:
http.createServer(function(req,res){
var _url = req.url;
var data = _url.indexOf(‘callback‘) >=0 ? req.url.match(reg)[0].substr(10)+‘("success")‘:‘success‘;
res.writeHead(200,{‘Content-Type‘:MINE_TYPE[‘txt‘],‘Access-Control-Allow-Origin‘:‘http://192.168.1.162:9988‘});
res.end(data);
}).listen(3003,‘192.168.1.162‘);
然后将jsonp请求的代码换回get请求的代码,重启这个js,切换到浏览器,刷新2个页面,再次点击请求,效果如下:
:9988
success
:8899
XMLHttpRequest cannot load http://192.168.1.162:3003/. The ‘Access-Control-Allow-Origin‘ header has a value ‘http://192.168.1.162:9988‘ that is not equal to the supplied origin. Origin ‘http://192.168.1.162:8899‘ is therefore not allowed access. // 缺德的3003,拒也就拒了,还喷了句:你又不是9988,我爱的是9988... 强行暴击
那么现在姿势明确了,两个姿势:正常请求+response设置允许域,jsonp。
对比两个方法,我们可以看出,前者讲究的是"你情我愿,两情相悦",我给了你家里的钥匙,你想来就来,离开的时候还能够满心欢喜,我不给别人家里的钥匙,他只能在门外懵逼;后者是"霸王硬上弓",管你同不同意,我背个大锤子来,你不开门是吧,我砸个它,强行进门,出去后我照样能够得到满足...(这很大一部分问题在门的质量,你要是厚厚的大铁门,人家没砸破,就被some叔叔按倒在地上了呢,而这个问题也正是jsonp得以实现的原理)
jsonp 实现原理
jsonp通过添加一个<script>标签,将该标签的src指向请求资源的接口,并且需要在请求中带上一个callback参数,script的src是不受浏览器的同源策略限制的,所以只要后端将数据包装在这个callback的方法里返回即可,于是我们有了这段代码:
server.js:
var data = _url.indexOf(‘callback‘) >=0 ? req.url.match(reg)[0].substr(10)+‘("success")‘:‘success‘;
// 如果包含callback参数,则将参数以callback的值为方法名包装成一个执行函数,否则直接返回数据
jsonp在jQuery中的实现为:
var oldCallbacks = [],
rjsonp = /(=)\?(?=&|$)|\?\?/;
// 默认jsonp设置
jQuery.ajaxSetup( {
jsonp: "callback",
jsonpCallback: function() {
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
this[ callback ] = true;
return callback;
}
} );
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
var callbackName, overwritten, responseContainer,
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
"url" :
typeof s.data === "string" &&
( s.contentType || "" )
.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
rjsonp.test( s.data ) && "data"
);
// jsonp 判断
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
// 生成 callback 名称
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
s.jsonpCallback() :
s.jsonpCallback;
// url处理 加入 callback
if ( jsonProp ) {
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
} else if ( s.jsonp !== false ) {
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
}
// 方法执行后取数据
s.converters[ "script json" ] = function() {
if ( !responseContainer ) {
jQuery.error( callbackName + " was not called" );
}
return responseContainer[ 0 ];
};
// Force json dataType
s.dataTypes[ 0 ] = "json";
// 添加callback这个方法
overwritten = window[ callbackName ];
window[ callbackName ] = function() {
responseContainer = arguments;
};
// 毁尸灭迹处理
jqXHR.always( function() {
// 如果之前不存在这个方法,删除
if ( overwritten === undefined ) {
jQuery( window ).removeProp( callbackName );
// 如果之前就存在这个方法,恢复
} else {
window[ callbackName ] = overwritten;
}
//
if ( s[ callbackName ] ) {
// 确保安全处理,不影响其他项
s.jsonpCallback = originalSettings.jsonpCallback;
// 预留着
oldCallbacks.push( callbackName );
}
// 如果是个函数,携带数据调用
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
overwritten( responseContainer[ 0 ] );
}
responseContainer = overwritten = undefined;
} );
// 归为script
return "script";
}
} );
// jsonp实现,生成script级之后移除
jQuery.ajaxTransport( "script", function( s ) {
// 这个函数指明了只处理跨域请求
if ( s.crossDomain ) {
var script, callback;
return {
send: function( _, complete ) {
script = jQuery( "<script>" ).prop( {
charset: s.scriptCharset,
src: s.url
} ).on(
"load error",
callback = function( evt ) {
script.remove();
callback = null;
if ( evt ) {
complete( evt.type === "error" ? 404 : 200, evt.type );
}
}
);
// 使用原生DOM操作避免一些 domManip ajax 问题
document.head.appendChild( script[ 0 ] );
},
abort: function() {
if ( callback ) {
callback();
}
}
};
}
} );
简单过一遍,就是这样了...然后扔出整理后的,不加各种判断的,简单的,只有20行代码的实现,帮助理解上面这么长长的一堆代码:
js:
function getInfo(url, callback, _callback){
url += url.indexOf(‘?‘)>=0 ? ‘&callback=‘ + callback : ‘?callback=‘+callback; // url处理
var overWriteContent;
var script = document.createElement(‘script‘);
script.src = url;
overWritten = window.dataBack;
window[callback] = function(){
overWriteContent = arguments[0];
}; // 生成callback方法,挂在window下
document.head.appendChild(script); // 添加script标签
script.onload = function(e){
document.head.removeChild(script); // 删除script标签
if(overWritten === undefined) delete window[callback]; // 销毁window下的callback方法
if(e.type === ‘load‘){
_callback(overWriteContent); // 带上数据执行回调
} else{
console.error(‘error:failed to load the resource‘);
}
}
}
学海无涯苦作舟,宝宝心里苦啊... 如有不正之处,感谢指正,同时欢迎大伙伴、小伙伴们交流讨论~
水平较渣,不喜勿喷,谢谢!
标签:
原文地址:http://www.cnblogs.com/ys-ys/p/5757905.html