本文是在了解了瀏覽器的同源規(guī)則之后,并學(xué)習(xí)一個破解這個規(guī)則的一個簡單有效的方法JSONP。主要通過阮一峰老師的博客學(xué)習(xí)
瀏覽器的同源規(guī)則
- 有這樣一個背景,如果你通過銀行的網(wǎng)站進(jìn)行的取錢的交易,而其他用戶可以通過某個渠道獲得你在銀行網(wǎng)站的信息,那將是很可怕的。
- 所以,1995年NetScape公司(火狐的前身),提出了瀏覽器的同源政策,目的是保護(hù)使用網(wǎng)站的用戶的信息安全。那么何謂同源呢
- 協(xié)議相同
- 域名相同
- 端口號相同
不過,隨著互聯(lián)網(wǎng)的發(fā)展,有些時候我們需要破解同源,所以要先學(xué)習(xí)一下,古老而有效的JSONP方法。
瀏覽器如何向服務(wù)器提交數(shù)據(jù)
有一天,程序員小白在自學(xué),看到JSONP很牛,就向大神程序員小黑請教。小黑,小黑,這個JSONP是啥啊,感覺很牛( ⊙ o ⊙ )!。
小黑扶了扶500度的眼鏡,摸了一下頭頂?shù)膸赘^發(fā),若有所思的問小白。
小白啊,你說,瀏覽器怎么向服務(wù)器提交數(shù)據(jù)啊,比如說,你要付款這個情形。
-
form表單啊,我規(guī)定
<form method="POST" action="/..."></form>,我不用get請求。<h5>您的賬戶余額是<span id="amount">200</span></h5> <button id="button">付款1塊錢</button> <form action="/pay" method="post"> <input type="text" name="number" value="1"> <input type="submit" value="付款"> </form> 恩,還是不錯的啊,知道用
POST發(fā)起請求。那你這提交完了之后,是不是還要在當(dāng)前頁面刷新一下,才能看到余額啊。-
……哎,是啊,不過我可以給你加一個
iframe,就在當(dāng)前頁面刷新<form action="/pay" method="post" target="result"> <input type="text" name="number" value="1"> <input type="submit" value="付款"> </form> <iframe name="result" src="about:blank" frameborder="0"></iframe>有什么反饋信息都在
iframe顯示。 恩,也還行,不過你為啥要把總額200寫死在頁面呢,不應(yīng)該動態(tài)從數(shù)據(jù)庫中獲得嗎
-
╮(╯▽╰)╭,稍等我改一哈
<h5>您的賬戶余額是<span id="amount">&&&amount&&&</span></h5> <button id="button">付款1塊錢</button> ... button.addEventListener('click', (e) => { let n = amount.innerText let number = parseInt(n, 10) let newNumber = number - 1 amount.innerText = newNumber }我用
&&&amount&&&占位符表示總額,服務(wù)器端可以如下處理var amount = fs.readFileSync('./db', 'utf-8') //從db中讀取 string = string.replace('&&&amount&&&', amount) //把占位的數(shù)據(jù)換成真的數(shù)據(jù) ... response.write(string) 恩,不錯,你再想想有沒有其他的方式也可以發(fā)送數(shù)據(jù)到服務(wù)器端啊,不用刷新頁面的那種
……還有其他的( ⊙ o ⊙ )??!
-
那我老黑我給你講講前輩程序員們試過的方法吧
用圖片發(fā)起get請求 let image = document.createElement('img') image.src = '/pay' image.onload = function() { alert('打錢成功') amount.innerText = amount.innerText - 1 } image.onerror = function() { alert('打錢失敗') }這種也是可以的,而且也會用提示給用戶,交互性還可以,不過只能發(fā)起
GET請求,哈哈,我就是秀一下黑科技,很少用啦…… (@ο@) 哇~這也可以,小黑,你好棒,又長見識啦,不過還是沒給我講JSONP啊,你是不是忘了……
沒忘啦,不要著急,接下來,就給你好好講講這個JSONP
動態(tài)創(chuàng)建JS腳本發(fā)數(shù)據(jù)
小白啊,你平常用的最多的是哪門語言啊
中文啊,英語不大好。
……我說編程的時候
呃呃,那個用的JavaScript多啊
-
好,那咱們就用js腳本發(fā)數(shù)據(jù)唄
//用js腳本發(fā)起請求 let script = document.createElement('script') script.src = '/pay' document.body.appendChild(script) script.onerror = function() { alert('failed') } ... //服務(wù)器端一般這么干 if(path === '/pay') { var amount = fs.readFileSync('./db', 'utf8') var newAmount = amount - 1 fs.writeFileSync('./db', newAmount) response.setHeader('Content-Type', 'application/javascript') response.statusCode = 200 response.write(` amount.innerText = amount.innerText - 1 `) response.end() }以上是js腳本的大致意思,細(xì)節(jié)不要深究,明白就行。注意一下,添加
script后,要記得document.body.appendChild(script) 不過,小黑啊,你這動態(tài)加上了
script沒錯,可是你每次都往我的html底部加js,這破壞我的html啊-
恩,小白啊,你思考能力還是可以的,目前確實有這個弊端,我給你處理一下
//用js腳本發(fā)起請求 let script = document.createElement('script') script.src = '/pay' document.body.appendChild(script) script.onload = function(e) { e.currentTarget.remove() //加載完了,就移除 } script.onerror = function(e) { alert('failed') e.currentTarget.remove() //加載完了,就移除 } 可以可以,小黑你這波操作可以的??熳屛乙娮R見識JSONP吧
-
好,這就給你變出來
button.addEventListener('click', (e) => { //用js腳本發(fā)起請求 let script = document.createElement('script') let functionName = 'wushao' + parseInt((Math.random()*100000), 10) window[functionName] = function (result) { if (result === 'success') { amount.innerText = amount.innerText - 1 } else { } } script.src = 'http://想訪問的另一個網(wǎng)站:端口號/pay?callback=' + functionName document.body.appendChild(script) script.onload = function(e) { e.currentTarget.remove() } script.onerror = function(e) { alert('failed') e.currentTarget.remove() } }) ヾ(?`Д′?)黑神,你這跨度有點大,咋變了了個大魔術(shù)。
-
O(∩_∩)O哈哈~,你讓我給你快點講的……,我給你講講細(xì)節(jié)吧
-
let functionName = 'wushao' + parseInt((Math.random()*100000), 10) 使用一個隨機函數(shù)構(gòu)建自己的函數(shù)名字,可以與服務(wù)器端代碼完美解耦,服務(wù)器端只需要,獲得查詢參數(shù)
?callback=functionName里面的functionName就可以了。//服務(wù)器端只需要這樣就可以了,不關(guān)心你寫的是什么函數(shù)名字 response.write(` ${query.callback}.call(undefined, 'success') `) window[functionName] = function (result) { }在window全局對象上添加functionName屬性,它的值是一個函數(shù),當(dāng)服務(wù)器端響應(yīng)回來后,瀏覽器端的寫的函數(shù)的參數(shù)就是服務(wù)器端的success,我們就知道我的數(shù)據(jù)成功了。
-
-
哇,厲害啊,不過你又犯了一個相同的錯誤啦,哈哈,每次要把添加的全局對象的屬性去掉哦~
script.onload = function(e) { e.currentTarget.remove() delete window[functionName] } script.onerror = function(e) { alert('failed') e.currentTarget.remove() delete window[functionName] } O(∩_∩)O哈!,這樣子就對了,小白啊,既然你學(xué)過jQuery,你試一試jQuery的寫法吧
-
(o)/~行,小黑,我也給你變一個
$.ajax({ url: "http://想訪問的另一個網(wǎng)站:端口號/pay", // The name of the callback parameter, as specified by the YQL service jsonp: "callback", // Tell jQuery we're expecting JSONP dataType: "jsonp", // Work with the response success: function (response) { if(response === 'success') { amount.innerText = amount.innerText - 1 } } }) 哎呦,不錯呦,小白~
O(∩_∩)O哈哈~,我就是Google的
jquery jsonp不過,這個可和ajax,沒啥關(guān)系啊,不明白為啥jquery為啥這么寫。
具體的代碼鏈接在============>傳送門
什么是JSONP呢
請求方是一個網(wǎng)站(瀏覽器端),響應(yīng)方是另一個網(wǎng)站(服務(wù)器端)
請求方動態(tài)的創(chuàng)建一個script腳本,src屬性是響應(yīng)方的地址,同時傳遞一個查詢查參數(shù)
?callbackName=functionName。對于callbackName和functionName,通常使用callback來代替callbackName,使用一串具有特殊含義的字母+隨機函數(shù)構(gòu)造functionName。-
響應(yīng)方根據(jù)收到的查詢參數(shù)callbackName=functionName,去構(gòu)造形如
2.1
functionName.call(undefined, 'success')2.2 或者直接
functionName.('success')這樣的響應(yīng)。
瀏覽器收到響應(yīng)之后,就會執(zhí)行
functionName.call(undefined, 'success')或者functionName.('success')然后,請求方就知道了他想要獲得的數(shù)據(jù)如何了。
這就是JSONP的原理
為什么JSONP不支持POST請求呢
答曰:
- JSONP通過動態(tài)創(chuàng)建script來實現(xiàn)的
- 動態(tài)創(chuàng)建script的時候,只能用get,沒辦法用post
接下來學(xué)習(xí)ajax啦~加油↖(ω)↗