跨域之jsonp
我们都知道使用<script>标签可以引入外部的JS文件,即使这个JS文件来自于其他的网站,比如我们引用存放在网络服务器上的jQuery框架。在这个过程中,我们已经实现跨域访问。像<script>标签这种本身具有跨域访问能力的标签还有<link>、<img>、<iframe>等。jsonp的实现原理就是利用<script>标签,实现了跨域访问。
先看一个例子:
先创建一个html文件,代码如下:
1 <body> 2 <p>名字:<span id="name"></span></p> 3 <script> 4 function test(obj) { 5 document.getElementById("name").innerHTML = obj.name; 6 } 7 </script> 8 </body>
此时在浏览器打开这个文件,页面中只有“名字:”这三个字符。
现在创建一个JS文件,test.js,其中的代码如下:
1 var person = { 2 name: "Jack", 3 age: 20 4 } 5 test(person);
通过<script>标签在html文件中引入这个test.js文件(添加到body结束标签之前):
1 <script src="test.js"></script>
再在浏览器中打开html文件就会看到:
如果把这个test.js放在百度的服务器上,在淘宝的页面上有一个test函数声明,然后动态地创建一个<script>标签来引用这个test.js,那么在淘宝的页面上就可以显示来自百度的服务器的数据,这就是jsonp的原理。当然引用的不一定非要是js文件,也可以是php、.net等文件,只要在这些文件中事先写好相应的代码即可。
下面用聚合数据的天气预报接口和新浪的城市查询接口为例来介绍jsonp。
首先是新浪的城市查询接口。
新浪的城市查询接口是:
http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js
可以直接在浏览器中打开,发现是一个名为remote_ip_info的Javascript对象,
var remote_ip_info = { "ret":1, "start":-1, "end":-1, "country":"\u4e2d\u56fd", "province":"\u6cb3\u5357", "city":"\u8bb8\u660c", "district":"", "isp":"", "type":"", "desc":"" };
那么在本地的测试页面中可以使用这个接口获取所在的城市,代码如下:
1 //获取当前城市 2 function getCity(url) { 3 var city = ''; 4 //创建一个script标签 5 var script = document.createElement("script"); 6 //设置script标签引用的url 7 script.setAttribute("src",url); 8 //将script标签添加到头部,也可以添加到其他地方 9 var head = document.getElementsByTagName('head')[0]; 10 head.appendChild(script); 11 //如果script标签加载完成,获取城市 12 script.onload = function() {//script.onload不兼容IE8及以下版本 13 city = remote_ip_info["city"]; 14 console.log(city); 15 }; 16 17 //移除创建的script标签 18 head.removeChild(script); 19 } 20 21 //该URL是新浪的获取当前城市的API,采用jasop方法获取数据 22 getCity("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=js");
下面是聚合数据的天气预报接口。
接口示例:http://op.juhe.cn/onebox/weather/query?cityname=%E6%B8%A9%E5%B7%9E&key=您申请的KEY
使用这个接口时至少要传入三个参数:
- cityname是要查询的城市名,可以用前面新浪的城市查询接口得到的数据。
- key是申请使用聚合数据天气预报接口时,给的APPKey。
- 另外还有一个callback参数,传入一个函数名,服务器会把查询后得到的数据拼接成函数调用的形式返回来,比如传入test函数名,则返回来test({"date":"2011-11-11","wind":"微风"}),所以要在测试页面声明好这个函数,以便对返回来的数据进行处理。
下面是代码:
html代码,将获得数据在页面中显示出来
1 <p id="chuanyi"></p> 2 <p id="yundong"></p> 3 <p id="ziwaixian"></p>
获得天气情况数据的代码,原理与获取当前城市的原理一样,不同的是要事先指定回调函数,并将函数名加到url的末尾。
1 function $(id) { 2 return document.getElementById(id); 3 } 4 5 //指定回调函数 6 function callback(data) { 7 $("chuanyi").innerHTML = "穿衣指数:" + data.result.data.life.info.chuanyi; 8 $("yundong").innerHTML = "运动指数:" + data.result.data.life.info.yundong; 9 $("ziwaixian").innerHTML = "紫外线:" + data.result.data.life.info.ziwaixian; 10 } 11 12 function getWeather(city) { 13 var url = 'http://op.juhe.cn/onebox/weather/query?cityname='+encodeURIComponent(city)+'&key=abbac0fc4fccbec34891de77bb1cfb4e&callback=callback';//该URL是聚合数据的天气预报API,采用jasop方法获取数据 14 console.log(encodeURIComponent(city)); 15 var script = document.createElement("script"); 16 script.setAttribute("src",url); 17 var head = document.getElementsByTagName('head')[0]; 18 head.appendChild(script); 19 head.removeChild(script); 20 }
有了以上代码,还不能获取到天气数据,因为getWeather()函数还没有调用。为了能将获取到的城市数据传给getWeather()函数,要在getCity()函数内部script的onload事件处理程序中调用getWeather()函数。
下面是调用getWeather函数返回的数据,一大串:
1 callback({ 2 "reason":"successed!", 3 "result":{ 4 "data":{ 5 "pubdate":"2016-11-11", 6 "pubtime":"11:00:00", 7 "realtime":{ 8 "city_code":"101180401", 9 "city_name":"许昌", 10 "date":"2016-11-11", 11 "time":"16:00:00", 12 "week":5, 13 "moon":"十月十二", 14 "dataUptime":1478853365, 15 "weather":{ 16 "temperature":"15", 17 "humidity":"64", 18 "info":"多云", 19 "img":"1" 20 }, 21 "wind":{ 22 "direct":"南风", 23 "power":"3级", 24 "offset":null, 25 "windspeed":null 26 } 27 }, 28 "life":{ 29 "date":"2016-11-11", 30 "info":{ 31 "chuanyi":["较冷","建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。"], 32 "ganmao":["较易发","天凉,昼夜温差较大,较易发生感冒,请适当增减衣服,体质较弱的朋友请注意适当防护。"], 33 "kongtiao":["较少开启","您将感到很舒适,一般不需要开启空调。"], 34 "xiche":["较适宜","较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"], 35 "yundong":["较适宜","天气较好,但考虑气温较低,推荐您进行室内运动,若户外适当增减衣物并注意防晒。"], 36 "ziwaixian":["弱","紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。"] 37 } 38 }, 39 "weather":[ 40 { 41 "date":"2016-11-11", 42 "info":{ 43 "day":["1","多云","17","","微风","06:53"], 44 "night":["0","晴","7","","微风","17:23"] 45 }, 46 "week":"五", 47 "nongli":"十月十二" 48 }, 49 { 50 "date":"2016-11-12", 51 "info":{ 52 "dawn":["0","晴","7","无持续风向","微风","17:23"], 53 "day":["0","晴","17","","微风","06:54"], 54 "night":["1","多云","8","","微风","17:23"] 55 }, 56 "week":"六", 57 "nongli":"十月十三" 58 }, 59 { 60 "date":"2016-11-13", 61 "info":{ 62 "dawn":["1","多云","8","无持续风向","微风","17:23"], 63 "day":["1","多云","17","","微风","06:55"], 64 "night":["0","晴","9","","微风","17:22"] 65 }, 66 "week":"日", 67 "nongli":"十月十四" 68 }, 69 { 70 "date":"2016-11-14", 71 "info":{ 72 "dawn":["0","晴","9","无持续风向","微风","17:22"], 73 "day":["1","多云","17","","微风","06:56"], 74 "night":["2","阴","5","","微风","17:21"] 75 }, 76 "week":"一", 77 "nongli":"十月十五" 78 }, 79 { 80 "date":"2016-11-15", 81 "info":{ 82 "dawn":["2","阴","5","无持续风向","微风","17:21"], 83 "day":["1","多云","13","","微风","06:57"], 84 "night":["1","多云","6","","微风","17:21"] 85 }, 86 "week":"二", 87 "nongli":"十月十六" 88 } 89 ], 90 "f3h":{ 91 "temperature":[ 92 { 93 "jg":"20161111170000", 94 "jb":"15" 95 }, 96 { 97 "jg":"20161111200000", 98 "jb":"13" 99 }, 100 { 101 "jg":"20161111230000", 102 "jb":"10" 103 }, 104 { 105 "jg":"20161112020000", 106 "jb":"10" 107 }, 108 { 109 "jg":"20161112050000", 110 "jb":"8" 111 }, 112 { 113 "jg":"20161112080000", 114 "jb":"7" 115 }, 116 { 117 "jg":"20161112110000", 118 "jb":"14" 119 }, 120 { 121 "jg":"20161112140000", 122 "jb":"16" 123 }, 124 { 125 "jg":"20161112170000", 126 "jb":"14" 127 } 128 ], 129 "precipitation":[ 130 {"jg":"20161111170000","jf":"0"}, 131 {"jg":"20161111200000","jf":"0"}, 132 {"jg":"20161111230000","jf":"0"}, 133 {"jg":"20161112020000","jf":"0"}, 134 {"jg":"20161112050000","jf":"0"}, 135 {"jg":"20161112080000","jf":"0"}, 136 {"jg":"20161112110000","jf":"0"}, 137 {"jg":"20161112140000","jf":"0"}, 138 {"jg":"20161112170000","jf":"0"} 139 ] 140 }, 141 "pm25":{ 142 "key":"Xuchang", 143 "show_desc":0, 144 "pm25":{ 145 "curPm":"50", 146 "pm25":"25", 147 "pm10":"51", 148 "level":1, 149 "quality":"优", 150 "des":"可正常活动。" 151 }, 152 "dateTime":"2016年11月11日16时", 153 "cityName":"许昌" 154 }, 155 "jingqu":"", 156 "jingqutq":"", 157 "date":"", 158 "isForeign":"0" 159 } 160 }, 161 "error_code":0 162 })
这篇文章讲解的很详细:jsonp协议原理深度解析
(完)