JSONP的歷史, 就是前端程序員爲了優化用戶體驗而造成的javascript
首先來看一下瀏覽器看見什麼會立刻進行請求?html
form
a
link
image
script
前三個就不說了,很簡單~, 重點說下image
和script
前端
服務器端代碼:java
response.setHeader('Content-Type', 'image/jpeg')
response.statusCode = 200
response.write(fs.readFileSync('./dog.jpeg'))
// 必須寫入一個真實的圖片,請求才會成功
複製代碼
前端代碼:node
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('打錢成功')
window.location.reload() // 刷新頁面
}
image.onerror = function(){
alert('打錢失敗')
}
// 利用圖片的加載成功和失敗,來判斷請求是否成功.
複製代碼
script
元素能夠做爲一種Ajax傳輸機制:只須設置script
元素的src
屬性(假如它還沒插入到document
中,須要插入進去),而後瀏覽器就會發送HTTP請求下載src屬性程序員
所指向的URL。使用script
元素進行Ajax傳輸的一個主要緣由是,它不受同源策略的影響(不受域名限制),所以能夠使用它們從其餘的服務器請求數據,第二個緣由是包含JSON編碼數據的響應體會解碼。 這種使用script
元素做爲Ajax傳輸的技術稱爲JSONPjson
說道script就和JSONP有關了後端
什麼是JSONP?跨域
全稱 JSON with Padding,用於解決AJAX跨域問題的一種方案。瀏覽器
因爲同源策略的限制,瀏覽器只容許XmlHttpRequest請求當前源(域名、協議、端口)的資源,而對請求script資源沒有限制。經過請求script標籤實現跨域請求,而後在服務端輸出JSON數據並執行回調函數,這種跨域的數據的方式被稱爲JSONP。
JSONP是一種非正式傳輸協議,該協議的一個要點就是容許用戶傳遞一個callback參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據,這樣客戶端就能夠隨意定製本身的函數來自動處理返回數據了。
具體步驟爲:
請求方: xxx.com 的前端程序員(瀏覽器)
響應方: yyy.com 的後端程序員(服務器)
請求方建立 script, src指向響應方, 同時傳遞一個查詢參數 ?callback=xxx
響應方根據查詢參數 callback, 構造形如
這樣的響應
瀏覽器接受到響應, 就會執行 xxx.call(undefined, '你要的數據')
那麼請求方就知道了他要的數據
JSONP的行業要求
服務器代碼
: 域名爲jack.com:8002
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.env.PORT || 8888
if (!port) {
console.log('請指定端口號好不啦?\nnode server.js 8888 這樣不會嗎?')
process.exit(1)
}
var server = http.createServer(function (request, response) {
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if (pathWithQuery.indexOf('?') >= 0) {
queryString = pathWithQuery.substring(pathWithQuery.indexOf('?'))
}
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method
if (path === '/') { // 當請求路徑爲 '/' 時, 返回一個index.html
var string = fs.readFileSync('./index.html', 'utf8')
var amount = fs.readFileSync('./db', 'utf8')
string = string.replace('&&&amount&&&', amount)
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.end(string)
} else if (path === '/pay') { // 當請求路徑問 '/pay'時
var amount = fs.readFileSync('./db', 'utf8')
var newAmount = amount - 1
fs.writeFileSync('./db', newAmouznt)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write(` ${query.callback}.call(undefined,'success') `)
response.end()
} else {
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.end()
}
})
server.listen(port)
複製代碼
上面的代碼
response.write(${query.callback}.call(undefined,'success')
)
第二個參數爲string, 也能夠叫作StringP, 若是換成JSON,就是JSONP.
${query.callbackName}.call(undefined,{
"success": true,
"left": ${newAmount}
})
// JSON: { "success": true,"left": ${newAmount}}
// 左padding: ${query.callbackName}.call(undefined,
// 右padding: )
複製代碼
前端代碼
: 域名爲frank.com:8001
<h5>您的帳戶餘額是 <span id='amount'>&&&amount&&&</span></h5>
<button id="button">付款</button>
<script> button.addEventListener('click', function (e) { let script = document.createElement('script') let functionName = 'frank' + parseInt(Math.random() * 100000, 10) // 前端聲明一個函數, 給後端調用, 函數名經過callback傳遞給後端 window[functionName] = function (result) { // 後端返回result, 能夠是json, 能夠是string if (result === 'success') { alert(`我獲得的結果是${result}`) amount.innerText = amount.innerText - 1 } else { alert('fail') } } script.src = 'http://jack.com:8001/pay?callback=' + functionName document.body.appendChild(script) script.onload = function (e) { e.currentTarget.remove() // 移除script delete window[functionName] } script.onerror = function () { alert('打錢失敗') e.currentTarget.remove() delete window[functionName] } }) </script>
複製代碼
由於JSONP是經過動態建立script來實現的, 動態建立的script只能用GET,不能用POST.