THE PROMISING LAND OF JSONP
Posted JavaScript与编程艺术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了THE PROMISING LAND OF JSONP相关的知识,希望对你有一定的参考价值。
PROMISE 極楽浄土的七宝楼阁
在 modern javascript 的“Pure Land”中 Promise
早已取代异步回调(callback hell),其趋势在 JavaScript 的婆娑世界中亦无可撼动。改革春风正在遍吹,例如在浏览器中,古老的 xhr
回调已被返回 Promise
的 fetch
解决;在服务端 Node.js 中,+8.0.0 之后的版本,util.promisify
已经开始对 fs
等其他模块的异步回调动手;来自 C# 的 async / await
更将此次 promisify
的战鼓擂的震天价响,“红旗招展,鞭炮齐鸣” 并非言过其实。正是由于 JavaScript 的不断换血和蜕化才使得其生态迸发勃勃生机,惹得万物萌动,春意盎然。
除了 Promise
的流行,将 jsonp
promisify
我们还可以得到什么好处呢?promisify
后我们可以随意控制和组合 jsonp
这股原本“邪恶”的异步流,煎炸焖烹溜煨炖,红烧清蒸卤煮盐焗碳烤,拔丝蜜汁糖醋任君选,哦不,应该是串行、并行、Race、线性错误处理机制,一切 Promise
携带的好处我们都将享受到。Delicious & Cooooool!
WHAT IS JSONP
以下解释摘自维基百科
JSONP (JSON with Padding or JSON-P) is used to request data from a server residing in a different domain than the client. It was proposed by Bob Ippolito in 2005.[2] JSONP enables sharing of data bypassing same-origin policy.
总而言之 JSONP 是一种获取跨域资源的方法。先介绍下跨域的基础知识:
你今天“跨域”了吗?
一、何谓“域”?
域即 origin,其定义为 “An origin is defined as a combination of URI scheme, host name, and port number”,即 origin 是 scheme、host name、port 的集合。公式表示为 origin = scheme "://" host [ ":" port ]
来自 rfc6454。
举个 rfc 官网的例子 https://tools.ietf.org/html/rfc6454#section-4,则 scheme = https
,host = tools.ietf.org
,port=443
。
二、何谓“跨域”?
跨域即 Cross Origin。先解释“同域”:“Two URIs are part of the same origin if they have the same scheme, host, and port”,scheme、host 和 port 三者都相同则同域,否则只要三者有其一不同,则称之为“跨域”。
三、为何限制“跨域”?
防止敏感信息比如 cookie 等被第三方窃取。
HOW JSONP WORKS
下面介绍下 jsonp
如何做到跨域获取资源。简单一句话有两点需解释。
一、为何 JSONP 可以”跨域“?
引用外部 JS 时,比如我们引入 jquery <script ></script>
,该资源来自 https://cdn.bootcss.com
,对于引用其的网址来说明显跨域了,但是为什么 jquery 还能正确执行呢,因为 script
天生就有跨域能力 ,jsonp
正是利用这一点达到跨域的能力。
注意:不能在 HTTPS 网站内加载非 HTTPS 的资源。原因
网站支持 HTTPS 是保护您的网站和用户免受攻击的重要一步,但混合内容会使这种保护失效
—— 来自 Chrome
所以你没法在 HTTPS 网站中注入 src
来自 HTTP 的脚本,但并不表示,JSONP 不支持跨域,因为这仅是 HTTPS 的保护策略导致的。反过来我们可以在非 HTTP 网站注入 src 是 HTTPS 的脚本,所以从这一点来看,JSONP 支持跨域并不是一句夸大其词的 slogan。
二、资源如何获取?
最重要的一点,跨域后资源如何获取?我们知道引入外部 JS 后,该 JS 会自动执行,且其执行环境和我们的 JS 处于同一个环境,即他可以执行我们自定义的全局函数。所以服务端只要和客户端协商好该函数,然后将 payload
即资源(通常是一个字面量对象),当做该函数的实参,当 JS 加载完毕,浏览器会自动执行该全局函数,而全局函数是我们自定义的,当然就能获取到该资源。
这个全局函数就是一个获取资源钩子,即 JSONP 中 P(Padding 或 Prefix,放在前面自然就是“前缀”)。注意 JSONP 中的 JSON,并非一定要是 JSON,因为只要能在 JS 环境中编译即可,即只要是一个有效的 JS 字面量即可。请注意:JSON 和 JS 的字面量对象是有区别的。
期中总结
JSONP
跨域必须施行以下两点,第一点:动态注入 script
,第二点:协商的全局函数。
第一点是为了跨域,第二点是为了获取资源。看一个具体的例子来验证下我们的猜想。先验证第二点。
协商的全局函数
我们去优酷抓一个下拉提示的 JSONP 链接:http://tip.soku.com/search_tip_1?jsoncallback=XBox.dUpdate&query=向往的&site=14,将其在新窗口打开,观察其返回值。
发现 jsoncallback
指向的 XBox.kUpdate
很有可能就是“全局函数”,即 JSONP
中的 P
。验证方法:在 youku 控制台上输入window.XBox.kUpdate
,发现确实存在该函数,输入 debug(window.XBox.kUpdate)
,然后修改输入框中的搜索词,发现浏览器 debugger 确实停在该函数内,则证实了第二点,有全局函数且资源下载完毕会执行该全局函数。
将 jsoncallback=XBox.kUpdate
改为 jsoncallback=whatever_func_you_like
试试,是不是返回值也变化了,说明该全局函数是“协商的”。
动态注入 script
youku 控制台输入 [...document.scripts].map(s => s.src).filter(src => src.startsWith('http://tip.soku.com')).length
,修改搜索词,重新执行,发现长度加一,则验证了我们的第一点确实是通过动态注入 script
标签实现的。
理解这两点, 我们就可以写出一段通过 jsonp
获取跨域资源的代码了。而我们今天的目标就是将这段代码 promisify
。
function handleResponse(response) {
console.log('I got a response throught JSONP. Woow how awesome I am:', response);
}
var script = document.createElement("script");
script.src = `http://tip.soku.com/search_tip_1?jsoncallback=handleResponse&query=向往的&site=14`;
document.querySelector('head').appendChild(script);
将上面代码复制粘贴到一个 HTTP 网站的控制台中执行,是否看到了最终效果?
“刘郎已恨蓬山远 更隔蓬山一万重” 以上是关于THE PROMISING LAND OF JSONP的主要内容,如果未能解决你的问题,请参考以下文章 急!求Canada-land of the maple tree 课文 The Fintech 250: The Top Fintech Startups Of 2018 [论文阅读] (21)S&P21 Survivalism: Systematic Analysis of Windows Malware Living-Off-The-Land (经典离地攻击 Land of Farms HDU - 5556 二分图匹配