Javascript 异步请求 ajax 实现跨域资源共享 CORS 的几种方法
问题描述
在当前域名 http://app.com
下的页面,使用 Jquery 的 .ajax()
异步访问另一个域名 http://test.com
。
$.ajax({
data: {test: "test"},
url: "http://test.com/index",
dataType: "json",
method: "post",
success: function (data) {
console.log(data);
},
error: function (error) {
console.log(error);
}
});
浏览器开发工具的控制台,会输出跨域的错误。
Access to XMLHttpRequest at 'http://test.com/index' from origin 'http://app.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST http://test.com/index net::ERR_FAILED
问题解决
一般有2种解决方案处理这个难题。
1. 服务端返回 Access-Control-Allow-Origin 头
就像控制台错误指出的一样:CORS(跨域资源共享)策略已阻止从原始站点 http://app.com
访问 http://test.com/index
处的 XMLHttpRequest 请求:请求头没有出现 Access-Control-Allow-Origin
。浏览器一般是使用 同源策略(same-origin policy) 的机制来处理资源之间的交互,它能阻隔恶意文档,减少可能被攻击的媒介。
服务器如 Nginx、Apache 或者 IIS等,服务端语言如 PHP、Java、Node 等,都能返回 Access-Control-Allow-Origin
请求头,请求头的格式是 Access-Control-Allow-Origin: *
(* 符号表示所有域名)或者 Access-Control-Allow-Origin:
http://app.com
(指定一个域名)。
2. 使用 jsonp
JSONP或JSON-P(JSON with Padding)是一种JavaScript技术,可通过加载 html 的 script
标签来请求数据。它是Bob Ippolito在2005年提出的,JSONP允许绕过同源策略共享数据,该策略不允许运行 JavaScript 代码读取从页面原始站点外部获取的媒体文档对象模型(DOM)元素或XMLHttpRequest数据。为了实现 jsonp,需要服务端和 Javascript 同时配合。
2.1 Javascript 实现 jsonp
jsonp 实际上是去构建一个 script 标签,指定 src,因此只能使用 get 请求,如果要传参数,可以将数据的键值对拼接在 url 上,必要时使用 encodeURIComponent 对参数编码。实现 jsonp 有以下几种方法。
2.1.1 使用原生js
script 标签的 src 属性,末尾加上时间参数,是为了防止出现缓存。
/**
* jsonp 回调成功的方法
* @param data
*/
function success(data) {
console.log(data);
}
var script = document.createElement("script");
// 注意 callback 的参数值,需要与回调的方法名字一样
script.src = "http://test.com/index?callback=success&test=test&_=" + (new Date()).getTime();
// 脚本加载成功的回调
script.onload = function() {
script.remove(); // 移除 script 标签
};
// 脚本加载失败的回调
script.onerror = function() {
script.remove(); // 移除 script 标签
};
document.head.appendChild(script);
注意要添加 callback 参数,并且参数值必须与回调方法名一致。
2.1.2 使用 script 标签
2.1.1 的代码相当于加载一个 script 标签,与下面的语句是相等的。
<script type="text/javascript">
/**
* jsonp 回调成功的方法
* @param data
*/
function success(data) {
console.log(data);
}
</script>
<script type="text/javascript" src="http://test.com/index?callback=success&test=test&_=13458766654879"></script>
2.1.3 使用 jquery 的 ajax
$.ajax({
data: {test: "test"},
url: "http://test.com/index",
dataType: "jsonp",
method: "get",
success: function (data) {
// 回调
console.log(data);
},
error: function (error) {
console.log(error);
}
});
或者使用 jsonpCallback 指定一个回调的方法名,如下:
/**
* jsonp 回调成功的方法
* @param data
*/
function success(data) {
console.log(data);
}
$.ajax({
data: {test: "测试"},
url: "http://test.com/index",
method: "get",
dataType: "jsonp",
jsonpCallback: "success",
error: function (error) {
console.log(error);
}
});
2.1.4 使用 jquery 的 getScript
/**
* jsonp 回调成功的方法
* @param data
*/
function success(data) {
console.log(data);
}
var url = "http://test.com/index?callback=success&test=test&_=" + (new Date()).getTime();
$.getScript(url, function( data, textStatus, jqxhr ) {
console.log( data ); // Data returned
console.log( textStatus ); // Success
console.log( jqxhr.status ); // 200
console.log( "Load was performed." );
});
2.2 服务端返回 jsonp 数据
以上面的 JavaScript 请求为例子,服务端需要返回这样格式的数据:
var data = {"username":"test","password":"123456"};
success(data);
或者可以简单一点:
success({"username":"test","password":"123456"});
success 表示 url 中 callback 的参数值(因此,script 的 src 属性,不一定要包含 callback 参数名,可以是其他名字,需要与服务端协商好),是 JavaScript的回调方法名,传回给 JS 的数据必须包含在 () 括号里面,这里是一个 json,也可以是其他格式的数据,如
success('test')
表示字符串,值为 test
。
success(123)
表示一个整型,值为 123
。
说到底,就是返回一个规范的 JavaScript,让浏览器去执行,只要把 jsonp 理解成是一个普通的 script 标签就行了,是不是很简单!