利用 JSONP 实现跨域调用
说道跨域调用,可能大家首先想到的或者听说过的就是 JSONP
了。
1.1 什么是JSONP
JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题。其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 <script>
标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料。用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句。
1.2 使用 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({ url: ‘http://localhost:3001/ajax/deal‘, data: data, dataType: ‘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(textStatus + ‘ ‘ + errorThrown); } }); });
服务器 3001 上对应的处理函数为:
1 app.get(‘/ajax/deal‘, function(req, res) { 2 console.log("server accept: ", req.query.name, req.query.id) 3 var data = "{" + "name:‘" + req.query.name + " - server 3001 process‘," + "id:‘" + req.query.id + " - server 3001 process‘" + "}" 4 var callback = req.query.callback //获得请求端回调函数 5 var jsonp = callback + ‘(‘ + data + ‘)‘ 6 console.log(jsonp) 7 res.send(jsonp) 8 res.end() 9 })
这里一定要注意 data 中字符串拼接,不能直接将 JSON 格式的 data 直接传给回调函数,否则会发生编译错误: parsererror Error: jsonpCallback was not called。
1.3 使用 <script>
标签原生实现 JSONP
经过上面的事件,你是不是觉得 JSONP 的实现和 Ajax 大同小异?
其实,由于实现的原理不同,由 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上对应的处理函数:
1 app.get(‘/jsonServerResponse‘, function(req, res) { 2 var cb = req.query.jsonp //这里得到请求页面的回调函数 3 console.log(cb) //思考一下为什么这里要这样写 4 var data = ‘var data = {‘ + ‘name: $("#name").val() + " - server 3001 jsonp process",‘ + ‘id: $("#id").val() + " - server 3001 jsonp process"‘ + ‘};‘ 5 var debug = ‘console.log(data);‘ //打印var data=""; 6 var callback = ‘$("#submit").click(function() {‘ + data + cb + ‘(data);‘ + debug + ‘});‘ 7 res.send(callback) //返回的是一个点击按钮的事件 8 res.end() 9 })
与上面一样,我们在所获取的参数后面加上 “ - server 3001 jsonp process” 代表服务器对数据的操作。从代码中我么可以看到,处理函数除了根据参数做相应的处理,更多的也是进行字符串的拼接。
-
2.4 JSONP 总结
至此,我们了解了 JSONP 的原理以及实现方式,它帮我们实现前端跨域请求,但是在实践的过程中,我们还是可以发现它的不足:
- 只能使用 GET 方法发起请求,这是由于
script
标签自身的限制决定的。 - 不能很好的发现错误,并进行处理。与 Ajax 对比,由于不是通过 XmlHttpRequest 进行传输,所以不能注册 success、 error 等事件监听函数。
-
使用 CORS 实现跨域调用
3.1 什么是 CORS?
Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以让网页设计师用一般的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另一方面,JSONP 可以在不支持 CORS 的老旧浏览器上运作。现代的浏览器都支持 CORS。
-
3.2 CORS 的实现
还是以 服务器 3000 上的请求页面向 服务器 3001 发送请求为例。
- 服务器 3000 上的请求页面 JavaScript 不变,服务器 3001上对应的处理函数:
-
1 app.post(‘/cors‘, function(req, res) { 2 res.header("Access-Control-Allow-Origin", "*"); //设置请求来源不受限制 3 res.header("Access-Control-Allow-Headers", "X-Requested-With"); 4 res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //请求方式 5 res.header("X-Powered-By", ‘ 3.2.1‘) 6 res.header("Content-Type", "application/json;charset=utf-8"); 7 var data = { 8 name: req.body.name + ‘ - server 3001 cors process‘, 9 id: req.body.id + ‘ - server 3001 cors process‘ 10 } 11 console.log(data) 12 res.send(data) 13 res.end() 14 })
3.3 CORS 中属性的分析
-
- Access-Control-Allow-Origin
The origin parameter specifies a URI that may access the resource. The browser must enforce this. For requests without credentials, the server may specify “*” as a wildcard, thereby allowing any origin to access the resource.
- Access-Control-Allow-Methods
Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.
- Access-Control-Allow-Headers
Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
3.4 CORS 与 JSONP 的对比
- CORS 除了 GET 方法外,也支持其它的 HTTP 请求方法如 POST、 PUT 等。
- CORS 可以使用 XmlHttpRequest 进行传输,所以它的错误处理方式比 JSONP 好。
- JSONP 可以在不支持 CORS 的老旧浏览器上运作。
- Access-Control-Allow-Origin
- 只能使用 GET 方法发起请求,这是由于