同源策略規定只在協議相同、域名相同、端口相同的狀況下,也就是兩個網頁同源時,才能讀寫對方的資源。這是爲了保證用戶的信息安全作出的限制,然而同源策略有時也會對合理的用途形成影響,那麼就須要想辦法規避同源策略帶來的影響。javascript
瀏覽器解析html頁面時,若是看到有如link、img、script等標籤時,就會自動向標籤對應路徑發起一個get請求以獲取想要獲得的資源。而用標籤發起的請求是不受同源策略限制的,利用這種特性,就能夠達到規避同源策略向目標網站發起請求的目的。html
例如,頁面中有一個script標籤:前端
<script src="http://demo.com/test"></script>
複製代碼
這個標籤就會向 demo.com 下的 test 路徑發起請求,而當服務器接收到請求後就會作出響應。固然通常這種標籤是經過JavaScript來動態建立的:java
const script = document.createElement('script')
script.src = 'http://demo.com/test'
document.body.appendChild(script)
複製代碼
這樣就能直接用js動態地發送請求了。須要注意的是,由於請求是經過script發送的,因此瀏覽器接收到響應時會當即執行請求到的結果。例如:數據庫
...
if (pathNoQuery === '/test') {
response.setHeader('Content-Type', 'application/javascript')
response.write('alert('這是 test 路徑返回的結果。')')
} else {
response.statusCode = 404
}
response.end()
...
複製代碼
這裏服務器接收到請求後返回了 alert(...)
的代碼,瀏覽器接收到結果後就會執行 alert(...)
,也就會彈出一個提示框。利用這一點,就能夠直接在接收到的script標籤中使用獲得的數據。假設頁面中就一個id爲balanceText的元素用來顯示用戶的餘額,服務器經過getResultFromDb函數來獲取數據庫中的數據:json
...
if (pathNoQuery === '/test') {
const rs = getResultFromDb()
response.setHeader('Content-Type', 'application/javascript')
response.write(`balanceText.textContent = '你還有${rs.balance}塊錢'`)
} else {
response.statusCode = 404
}
response.end()
...
複製代碼
這樣每次向服務器請求時,服務器就會取得數據庫中的數據並返回更改對應元素文本的執行語句,瀏覽器接收響應後執行語句,便可在頁面中顯示出接收到的數據。後端
然而這種方法很使人困惑,展現數據明明應該是前端應該作的事,卻被後端作了。並且後端爲了展現數據還須要瞭解前端的頁面結構。而這也致使後端與前端的耦合度過高,後端的響應幾乎只能爲這一個頁面服務。瀏覽器
那麼解決這個問題的思路天然是下降先後端的耦合度,也就是解耦。前端的代碼就應該放到前端去寫,然後端只須要將數據交給前端就好,不須要作額外的事情。安全
解決的辦法是在前端代碼中定義一個函數,也就是所謂的回調函數。服務器返回的代碼中只須要執行這個函數,而前端想要獲取的數據只須要經過參數傳遞到這個函數中便可。這裏就能夠修改一下以前的代碼:服務器
前端:
const cbFunc = (result) => {
balanceText.textContent = `你還有${result}塊錢`
}
const jsonpScript = document.createElement('script')
jsonpScript.src = 'http://demo.com/test?callback=cbFunc'
document.body.appendChild(jsonpScript)
複製代碼
後端:
...
if (pathNoQuery === '/test') {
const rs = getResultFromDb()
response.setHeader('Content-Type', 'application/javascript')
response.write(`${queryObject.callback}(${rs.balance})`)
} else {
response.statusCode = 404
}
response.end()
...
複製代碼
這裏前端請求時在查詢參數中傳遞了一個callback參數,記錄回調函數的函數名。後端只須要使用這個函數名返回一個調用回調函數的語句就行了。與此同時請求的數據也做爲參數被傳遞迴給回調函數,接着只須要在回調函數中使用這個參數就能夠獲得想要的數據了。這樣後端就只須要專一於返回數據而無需操心前端的代碼,前端拿到數據也就能夠放心地隨心所欲了。
那麼至此爲止,上文說了這麼多,和JSONP有什麼關係?
在這裏總結一下:
這個請求的過程就是JSONP。