JSONP是什麼

JSONP 是什麼

Jsonp (JSON with Padding) 是 json 的一種"使用模式",可讓網頁從別的域名(網站)那獲取資料,即跨域讀取數據。javascript

需求:點擊按鈕付款一塊錢

<h5>您的帳戶餘額是<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款</button>
複製代碼
  • form 發請求
<form action="/pay" method="POST" target="result">
  <input type="submit" value="付款">
</form>
<iframe name="result" src="about:black" frameborder="0" height="200"></iframe>
複製代碼

後端部分代碼:html

if(path === '/'){
  let string=fs.readFileSync('./index.html','utf8')
  let amount=fs.readFileSync('./db','utf8')
  string=string.replace('&&&amount&&&',amount)
  response.statusCode = 200
  response.setHeader('Content-Type', 'text/html;charset=utf-8')
  response.write(string)
  response.end()
}else if (path === '/pay') {
  let amount = fs.readFileSync('./db', 'utf8')
  let number = amount - 1
  if(Math.random() > 0.5){	//假設大於0.5就成功
    fs.writeFile('./db', number)
    response.write('success')
  }else{
    response.write('fail')
  }
  response.end()
}
複製代碼

form 表單提交以後必定會刷新當前頁面,這樣用戶體驗很差,利用 iframe 局部刷新頁面,優化用戶體驗。前端

有沒有想過,不返回 HTML,返回 JSjava

  • 方案一:用圖片造 get 請求

瀏覽器有個特色一旦發現你在內存裏建立了一個 img,就會去請求這個 img ,這個方法能夠悄無聲息的創造一個請求,但有個缺陷沒法設置 POST。那就 發GET請求吧,總比用 iframe 刷新頁面好。程序員

button.addEventListener('click', () => {
  let image = document.createElement('img')
  image.src = './pay'
  image.onload = function(){
    alert('付款成功')
    amount.innerText = amount.innerText -1
  }
  image.onerror = function(){
    alert('付款失敗')
  }
})
複製代碼

瀏覽器怎麼知道一個圖片請求是成功仍是失敗呢?狀態碼說的很清楚,假設若是成功就返回 200,失敗返回 400ajax

if (path === '/pay') {
  let amount = fs.readFileSync('./db', 'utf8')
  let number = amount - 1
  if(Math.random() > 0.5){
    fs.writeFile('./db', number)
    response.setHeader('Content-Type', 'image/png')
    response.statusCode = 200
    response.write(fs.readFileSync('./th.jpg'))
  }else{
    response.statusCode = 400
    response.write('fail')
  }
  response.end()
}
複製代碼

這樣就作到了無刷新的也沒有用什麼特殊的技術,只用了一個小技巧就是建立了一個 img 用它發請求。如今的問題就是沒法 POST,由於瀏覽器沒有給咱們一個接口讓咱們 POST,因此那就只好 GET 了, 這種方法只能知道成功或失敗,不能知道更多的數據。json

  • 方案二:用 scriptget 請求

這種方法必定要把 script 放到頁面裏面,瀏覽器纔會發起請求。怎麼樣才能知道是請求成功仍是失敗了呢,固然 script 也有 onloadonerror 事件。後端

button.addEventListener('click', () => {
  let script = document.createElement('script')
  script.src = '/pay'
  document.body.appendChild(script)
  script.onload = function(){
    alert('付款成功')
    amount.innerText = amount.innerText -1
  }
  script.onerror = function(){
    alert('付款失敗')
  }
}
複製代碼

改進以後的好處就是不用返回圖片了,返回一個字符串就能夠了,這樣請求就會快一點了。跨域

點擊 button 後,就會去建立一個 script 標籤會放在頁面的最後方,因爲頁面中出現了一個 script ,瀏覽器就會將 /pay 裏面的內容執行掉。這樣就不須要去監聽 onload 事件,就監聽 onerror 事件就能夠了。 onload 之間由 /pay 裏面的內容執行,服務器直接返回了在瀏覽器執行的一個 js 字符串(代碼)。瀏覽器

if (path === '/pay') {
  let amount = fs.readFileSync('./db', 'utf8')
  let number = amount - 1
  if(Math.random() > 0.5){
    fs.writeFile('./db', number)
    response.setHeader('Content-Type', 'application/javascript')
    response.statusCode = 200
    response.write(`
      alert("付款成功")
      amount.innerText = amount.innerText -1
    `)
  }else{
    response.statusCode = 400
    response.write('fail')
  }
  response.end()
}
複製代碼

不過還有一個問題就是沒點擊一次按鈕,頁面中都會多出現一個 script ,雖說不會出現 BUG,可是會很難看,因此在請求成功或失敗以後再刪除它。

button.addEventListener('click', () => {
  let script = document.createElement('script')
  script.src = '/pay'
  document.body.appendChild(script)
  script.onload = function(e){
    e.currentTarget.remove()
  }
  script.onerror = function(e){
    alert('付款失敗')
    e.currentTarget.remove()
  }
})
複製代碼

這就是整個的完整方案,當用戶點擊一個動做的時候,生成一個 script ,而後 scriptsrc 就是要請求的路徑。而後把 script 放到頁面裏,這樣瀏覽器就是去發起一個這樣路徑的 GET 請求(沒辦法 POST )。若是這個請求成功了,那麼它首先會執行那個服務器返回的 javascript 響應,這個響應就是操做頁面的局部刷新。

這種技術就叫作 SRJ - Server Rendered JavaScript,服務器返回的 JavaScript。這個就是在 AJAX 出現以前用的無刷新局部更新頁面內容。

上面的這個 SRJ 方案,若是沒有作任何的安全措施的話,任何一個網站均可以去請求這個 API 操做付款,因此像付款這些重要的操做要使要 POSTGET 太容易僞造了。

JSONP

上面都是都一個網站的前端和後端交流,那若是這個網站的前端想和另外一個域名下的接口交流怎麼辦呢?

前端給後端一個函數,而後後端調用這個函數,要執行的代碼不要管,而後返回一個結果。那後臺怎麼知道這個函數名呢,咱們能夠在請求的時候傳參。

window.yyy = function(result) {
  if(result === 'success'){
    amount.innerText = amount.innerText - 1
  }else{

  }
}
button.addEventListener("click", () => {
  let script = document.createElement("script");
  script.src = 'http://jackma.com:8002/pay?callback=yyy'
  document.body.appendChild(script)
    script.onload = function(e) {
    e.currentTarget.remove()
  }
  script.onerror = function(e) {
    e.currentTarget.remove()
  }
})
複製代碼
if (path === '/pay') {
  let amount = fs.readFileSync('./db', 'utf8')
  let number = amount - 1
  fs.writeFile('./db', number)
  response.setHeader('Content-Type', 'application/javascript')
  response.statusCode = 200
  response.write(`
    ${query.callback}.call(undefined, 'success')
  `)
  response.end()
}
複製代碼

給什麼就調什麼,這樣就徹底耦合了, 這就叫作 JSONPJSONP 要結決的一個問題就是兩個網站之間怎麼交流,咱們用一個 script 標籤就能夠交流了, script 標籤是不受域名限制的。既然不受限制,就能夠告訴對方網站我要請求一個數據,對方給數據以後再調用咱們準備的函數,把參數傳到函數的第一個參數裏面,咱們就能夠獲得了。

請求方:frank.com 的前端程序員(瀏覽器)

響應方:jack.com 的後端程序員(服務器)

  1. 請求方建立 scriptsrc 指向響應方,同時傳一個查詢參數 ?callback=yyy

    yyy 通常是要隨機數,這樣就不用出現函數名重複的問題。

  2. 響應方根據查詢參數 callback,構造形如

    1. yyy.call(undefined, '你要的數據')
    2. yyy('你要的數據') 這樣的響應
  3. 瀏覽器接收到響應,就會執行 yyy.call(undefined, '你要的數據')

  4. 那麼請求方就知道了他要的數據

這就是 JSONP

jQuery 的寫法:

$.ajax({
  url: "http://jack.com:8002/pay",
  dataType: "jsonp",
  success: function( response ) {
    if(response === 'success'){
      amount.innerText = amount.innerText - 1
    }
  }
})
複製代碼

JSONP 爲何不支持 POST 請求

由於 JSONP 是經過動態建立 script 實現的,動態建立 script 的時候只能用 GET 請求,沒有辦法用POST 請求。

相關文章
相關標籤/搜索