前端跨域请求方法整理
什么是跨域请求
浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用。一般的,只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用。
我们利用 NodeJs 创建了两个服务器,分别监听 3000、 3001 端口(下面简称 服务器3000 与 服务器3001 ),由于端口号不一样,这两个服务器以及服务器上页面通信构成了跨域请求。
在服务器3000 上有如下的页面:
ajaximage.png服务器3000 上的请求页面中包含如下 JavaScript 代码:
$(function () {
$("#submit").click(function () {
var data = {
name: $("#name").val(),
id: $("#id").val()
};
$.ajax({
type: 'POST',
data: data,
url: 'http://localhost:3000/ajax/deal',
dataType: 'json',
cache: false,
timeout: 5000,
success: function (data) {
console.log(data)
},
error: function (jqXHR, textStatus, errorThrown) {
console.log('error ' + textStatus + ' ' + errorThrown);
}
});
});
});
服务器3000 对应的处理函数为:
app.post('/ajax/deal', function (req, res) {
console.log('server accept', req.body.name, req.body.id);
var data = {
name: req.body.name + '- server 3000 process',
id: req.body.id + '- server 3000 process'
};
res.send(data);
res.end();
})
请求页面返回结果:
ajaximage2.png此处数据处理成功。
此时让 服务器3000 上的页面向 服务器 3001 发起请求:
$.ajax({
...
url: 'http://localhost:3001/ajax/deal',
...
});
服务器3001 对应的处理函数与 服务器3000 类似:
app.post('/ajax/deal', function (req, res) {
console.log('server accept', req.body.name, req.body.id);
var data = {
name: req.body.name + '- server 3001 process',
id: req.body.id + '- server 3001 process'
};
res.send(data);
res.end();
})
结果如下:
ajaximage3.png结果由于端口号不同,发生了跨域请求。
需要注意的是,服务器 3001 控制台有输出:
server accept: chiaki 3001
这说明跨域请求并非是浏览器限制了发起跨站请求,而是请求可以正常发起,到达服务器端,但是服务器返回的结果会被浏览器拦截。
利用 JSONP 实现跨域调用
JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题。其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 script 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。
1.使用 jQuery 集成的 $.ajax 实现 JSONP 跨域调用
依然是上面的例子,我们将 服务器 3000 上的请求页面的 JavaScript 代码改为:
// 回调函数
function jsonpCallback(data) {
console.log('jsonpCallback:' + data.name)
}
$("#submit").click(function () {
var data = {
name: $("#name").val(),
id: $("#id").val()
};
$.ajax({
// type: 'POST',
data: data,
url: 'http://localhost:3000/ajax/deal',
dataType: 'jsonp', //修改为jsonp
cache: false,
timeout: 5000,
// jsonp 字段含义为服务器通过什么字段获取回调函数的名称
jsonp: 'callback',
// 声明本地回调函数的名称,jquery 默认随机生成一个函数名称
jsonpCallback: 'jsonpCallback',
success: function (data) {
console.log("ajax success callback: " + data.name);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log('error ' + textStatus + ' ' + errorThrown);
}
});
});
服务器 3001 上对应的处理函数为:
app.post('/ajax/deal', function (req, res) {
/* req.body.name -> req.query.name
jsonp实质为script的src链接:
/ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1 */
console.log('server accept', req.query.name, req.query.id);
var data = "{" + "name:'" + req.query.name + " - server 3001 process'," + "id:'" + req.query.id + " - server 3001 process'" + "}"
var callback = req.query.callback
var jsonp = callback + '(' + data + ')'
console.log(jsonp)
res.send(jsonp)
res.end()
})
这里一定要注意 data 中字符串拼接,不能直接将 JSON 格式的 data 直接传给回调函数,否则会发生编译错误: parsererror Error: jsonpCallback was not called。
其实脑海里应该有一个概念:利用 JSONP 格式返回的值一段要立即执行的 JavaScript 代码,所以不会像 ajax 的 XmlHttpRequest 那样可以监听不同事件对数据进行不同处理。
处理结果如下所示:
ajaximage6.png2. 使用 script 标签原生实现 JSONP
由于实现的原理不同,由 JSONP 实现的跨域调用不是通过 XmlHttpRequset 对象,而是通过 script 标签,所以在实现原理上,JSONP 和 Ajax 已经一点关系都没有了。看上去形式相似只是由于 jQuery 对 JSONP 做了封装和转换。
比如在上面的例子中,我们假设要传输的数据 data 格式如下:
{
name: "chiaki",
id": "3001"
}
那么数据是如何传输的呢?HTTP 请求头的第一行如下:
GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1
可见,即使形式上是用 POST 传输一个 JSON 格式的数据,其实发送请求时还是转换成 GET 请求。
其实如果理解 JSONP 的原理的话就不难理解为什么只能使用 GET 请求方法了。由于是通过 script 标签进行请求,所以上述传输过程根本上是以下的形式:
<script src = 'http://localhost:3001/ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032'></script>
这样从服务器返回的代码就可以直接在这个 script 标签中运行了。下面我们自己实现一个 JSONP:
服务器 3000请求页面的 JavaScript 代码中,只有回调函数 jsonpCallback:
function jsonpCallback(data) {
console.log("jsonpCallback: "+data.name)
}
服务器 3000请求页面还包含一个 script 标签:
<script src='http://localhost:3001/jsonServerResponse?jsonp=jsonpCallback'></script>
服务器 3001上对应的处理函数:
app.post('/jsonServerResponse', function (req, res) {
var cb = req.query.jsonp;
console.log(cb);
var data = 'var data = {' + 'name: $("#name").val() + " - server 3001 jsonp process",' + 'id: $("#id").val() + " - server 3001 jsonp process"' + '};'
var debug = 'console.log(data);'
var callback = '$("#submit").click(function() {' + data + cb + '(data);' + debug + '});'
res.send(callback)
res.end()
})
与上面一样,我们在所获取的参数后面加上 “ – server 3001 jsonp process” 代表服务器对数据的操作。从代码中我么可以看到,处理函数除了根据参数做相应的处理,更多的也是进行字符串的拼接。
最终的结果为:
ajaximage4.pngJSONP 总结
至此,我们了解了 JSONP 的原理以及实现方式,它帮我们实现前端跨域请求,但是在实践的过程中,我们还是可以发现它的不足:
- 只能使用 GET 方法发起请求,这是由于 script 标签自身的限制决定的。
- 不能很好的发现错误,并进行处理。与 Ajax 对比,由于不是通过 XmlHttpRequest 进行传输,所以不能注册 success、 error 等事件监听函数。
使用 CORS 实现跨域调用
Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。
CORS 的实现
服务器 3000 上的请求页面 JavaScript 不变,如下:
$(function() {
$("#submit").click(function() {
var data = {
name: $("#name").val(),
id: $("#id").val()
};
$.ajax({
type: 'POST',
data: data,
url: 'http://localhost:3001/cors',
dataType: 'json',
cache: false,
timeout: 5000,
success: function(data) {
console.log(data)
},
error: function(jqXHR, textStatus, errorThrown) {
console.log('error ' + textStatus + ' ' + errorThrown);
}
});
});
});
服务器 3001上对应的处理函数:
app.post('/cors', function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
var data = {
name: req.body.name + ' - server 3001 cors process',
id: req.body.id + ' - server 3001 cors process'
}
console.log(data)
res.send(data)
res.end()
})
在服务器中对返回信息的请求头进行了设置。
最终的结果为:
ajaximage5.pngCORS 与 JSONP 的对比
- CORS 除了 GET 方法外,也支持其它的 HTTP 请求方法如 POST、 PUT 等。
- CORS 可以使用 XmlHttpRequest 进行传输,所以它的错误处理方式比 JSONP 好。
- JSONP 可以在不支持 CORS 的老旧浏览器上运作。
网友评论