什么是JSONP

JSONP(JSON with Padding)是一种解决Ajax跨域通信的手段。

为了隔离恶意文件,保证用户安全,主流浏览器实现了同源策略。

同源策略限制了请求的主机、域名、协议和端口必须与当前加载的文档相同,不在同一个源加载的文档或脚本不能被访问。

同源检测示例:

以当前所在页面为例:https://www.rdtoc.com/tutorial/json-jsonp-tutorial.html

目标URL 结果 原因
https://www.rdtoc.com/tutorial/markdown-tutorial.html 成功
https://www.rdtoc.com/dir/inner/another.html 成功
http://www.rdtoc.com/secure.html 失败 http、https协议不同
https://store.rdtoc.com:8080/dir/etc.html 失败 默认80、8080端口不同
https://tools.rdtoc.com/dir/other.html 失败 tools、store子域名不同

但在实际开发中,不可避免需要进行跨域访问。此时就需要用到jsonp。

JSONP 原理

借助script 标签的src属性来实现跨域,由于使用script标签的src属性,因此只支持get方法。

浏览器->浏览器: 注册yqlCallback(jsonData)方法
浏览器->服务器: Script标签 GET/
服务器->服务器: 通过callback参数拿到回调方法yqlCallback
服务器->服务器: 将jsonData传入回调方法\n  如yqlCallback(jsonData)
服务器->浏览器: 返回请求结果:可执行JavaScript
浏览器->浏览器: Script标签自动执行,拿到数据
  1. 首先在客户端注册一个名为yqlCallback的callback方法,并把方法名传递给服务端;
  2. 服务端拿到方法名并以要传递的数据作为参数,生成一段可执行JavaScript代码返回给客户端,如yqlCallback(jsonData)。
  3. 客户端script标签自动执行yqlCallback(jsonData)方法,以此拿到服务端的数据。

1. Script 标签跨域示例

  • 这是一个调用雅虎天气api的例子
  • 回调方法必须在引用跨域script之前执行
<script>
  var yqlCallback = function(data) {
        alert(JSON.stringify(data))
  };
</script>

<script src="https://query.yahooapis.com/v1/public/yql?q=select wind from weather.forecast where woeid in (select woeid from geo.places(1) where text='beijing, cn')&format=json&callback=yqlCallback"></script>

2. JavaScript 跨域示例

<script>
    function yqlCallback(data) {
        alert(JSON.stringify(data))
    }

    var script = document.createElement('script');
    script.src = "https://query.yahooapis.com/v1/public/yql?q=select wind from weather.forecast where woeid in (select woeid from geo.places(1) where text='beijing, cn')&format=json&callback=yqlCallback"
    document.getElementsByTagName('head')[0].appendChild(script);
</script>

3. jQuery 跨域示例

<script src="https://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
<script>
    function yqlCallback(data){
        alert(JSON.stringify(data))
    }
    $.ajax({
        url: "https://query.yahooapis.com/v1/public/yql?q=select wind from weather.forecast where woeid in (select woeid from geo.places(1) where text='beijing, cn')&format=json",
        dataType: "jsonp",
        jsonpCallback: "yqlCallback"
    });
</script>