實作 JSONP

時代的眼淚?

原理簡述

利用 <script>src="..." 可以跨網域存取資料的特性,搭配 callback function 來實現跨域請求的技術。

要注意的是 JSONP 只能應用在 GET,沒辦法 POST,因為只有 GET 會透過網址帶資料給後端(可以參考這裡

接下來我會分別實作「後端」跟「前端」實際在使用 JSONP 的時候會怎麼做設定。

後端該做的事

大致的思路是這樣:

  1. 準備好要給前端的資料
  2. 取得 url 中的 query 字串
  3. 根據 query 回傳對應的內容(其實就是執行一個 function,讓前端可以透過 function 裡的參數拿到資料)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 建立 server 的模組
const http = require('http')
// 解析 url 的模組
const url = require('url');
// 要傳給前端的資料(物件)
const data = {
name: 'PeaNu',
age: 23
}
// 伺服器設定
const server = http.createServer((req, res) => {
// 解析 url => {callback: 'getUser'}
const queryObject = url.parse(req.url, true).query
// 存取 callback 屬性 => 'getUser'
const yourFunction = queryObject.callback
// 把資料傳入(記得要轉成純字串) => getUser(data)
res.write(`${yourFunction}(${JSON.stringify(data)})`)
// 送出 response
res.end()
})
// 聽 5000 這個 port
server.listen(5000)

前端該做的事

大致的思路是這樣:

  1. 建立一個用來接收資料的 function(getUser
  2. 開一個 <script>src 向伺服器發出請求,並帶上 callback 參數來對應到第一步設好的 function

這裡要特別強調一下順序:先定義 function => 發出 request

不然在發 request 的時候 function 根本就還沒被定義,就等著吃 error。

備註:除非你設定 <script asnyc> 強制轉成非同步,不然就一定要按照上面的順序。

1
2
3
4
5
6
7
8
<script>
// 在這裡定義好要接收資料的 function
function getUser(data) {
console.log(data)
}
</script>
<!-- 這裡定義 callback 參數的值,也就是上面的 function -->
<script src="http://localhost:5000/callback=getUser"></script>

如果順利的話,最後就能成功拿到資料:

jsonp

備註

在這裡紀錄一下實作時碰到的問題。

本來我是想分成:

  1. 如果有帶上 callback 參數,後端就 回傳對應的 function
  2. 如果沒有帶參數,後端就 回傳純字串的 JSON 資料

所以後端的部分設定成這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 伺服器設定
const server = http.createServer((req, res) => {
// {callback: 'getUser'}
const queryObject = url.parse(req.url, true).query
// 'getUser'
const yourFunction = queryObject.callback
// 檢查前端有沒有帶上 callback 參數
if(yourFunction) {
// 把資料傳入 => getUser(data)
res.write(`${yourFunction}(${JSON.stringify(data)})`)
// 送出 response
res.end()
} else {
// 回傳 JSON 資料
res.write(JSON.stringify(data))
// 送出 response
res.end()
}
})

在有帶上 callback 參數的時候沒有問題,但是沒有的時候就會出現 CORB 的錯誤:

corb

本來以為是回傳內容的問題,所以我又把 res.write(JSON.stringify(data)) 改成 res.write('hi')

error

簡單來說,如果你想要用這種塞在 <script> 裡面的方式來傳 JSON 資料,那就 一定要用 JSONP 來傳,不然瀏覽器會把 response 給擋下來。

至於第二個 res.write('hi') 的結果也告訴我們,如果不是 JSON 資料的話就沒關係,但是這樣就沒有什麼意義了,因為等於前端只拿到一個裡面寫了 hi 的 js 檔案,所以最後執行這個檔案的時候才會噴 Uncaught ReferenceError

套件大總匯 放在 form 裡面的 button
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×