探索 Jsonp 的實現原理

1:什麼是 Jsonp

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

2: Jsonp 的實現原理

Jsonp 的實現原理是經過動態建立一個 script 標籤來實現的。
複製代碼

3:什麼是跨域

跨域是由於同源策略引發的,同源指的是:協議、域名、端口號。
複製代碼

3-1:不容許跨域的狀況

example 描述 是否容許跨域
h ttp://www.a.com/a.js
h ttps://www.a.com/b.js
協議不同
h ttp://www.a.com/a.js
h ttp://www.b.com/b.js
域名不同
h ttp://www.a.com/a.js
h ttp://www.a.com:8080/b.js
端口不同
h ttp://www.a.com/a.js
h ttp://www.192.1.1.168.com/b.js
域名跟域名對應的IP同樣
h ttp://www.a.com/a.js
h ttp://bb.a.com/b.js
主域名跟二級域名

3-2:跨域不能訪問一下操做

  • Cookie、Storage、IndexDB 沒法讀取
  • Ajzx 不能接收響應
  • 沒法拿到 Dom 對象 (如 iframe中的dom)

同源策略是瀏覽器的行爲,是爲了保護本地數據不被JavaScript代碼獲取回來的數據污染,所以攔截的是客戶端發出的請求回來的數據接收,即請求發送了,\color{red}{服務器響應了,可是沒法被瀏覽器接收}javascript

4: 藉助 node.js 實現 Jsonp

4-1:基於 XMLHttpRequest 實現的請求

客戶端:index.htmlhtml

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="xhr">xhr</button>
  <button id="jsonp">jsonp</button>
  <script> const XHR_BUTTON = document.querySelector('#xhr') XHR_BUTTON.onclick = () => { myXhr('GET', 'http://localhost:3000/test?type=1', (body) => { console.log(body) }) } function myXhr(method, url, callback) { const XHR = new XMLHttpRequest() XHR.onreadystatechange = () => { console.log(XHR.readyState, XHR.status) if (XHR.status === 200 && XHR.readyState === 4) { console.log(XHR.responseText) } } XHR.open(method, url, true) XHR.send() } </script>
</body>
</html>
複製代碼

服務端: server.js前端

const http = require('http')
const url = require('url')

let source = [
  {
    type: 1,
    value: '標題一'
  },
  {
    type: 2,
    value: '標題二'
  }
]

// 開啓一個服務
const app = http.createServer((req, res) => {
  let pathName = url.parse(req.url).pathname;

  if (pathName === '/favicon.ico') {  // 排除 favicon.ico 的請求
    res.end()
    return
  }

  let queryArr = url.parse(req.url).query.split('&')  // 獲取路由參數
  let query = {}

  queryArr.forEach(item => {
    let itemArr = item.split('=')
    query[itemArr[0]] = itemArr[1]
  })

  let body = source.filter(item => item.type === +query.type)

  // 響應數據
  setTimeout(() => {
    const BODY = JSON.stringify(body)
    const JSONP_NAME = query.jsonpName
    
    res.setHeader("Content-type","application/json"); // 解決亂碼問題
    if (JSONP_NAME) {
      res.write(`${ JSONP_NAME }(${ BODY })`)
    } else {
      res.write(BODY)
    }
    
    res.end()
  }, 1000)
})

// 監聽 3000 端口
app.listen(3000, () => {
  console.log('3000端口已被開啓')
})

複製代碼
  • 啓動服務端 node server.js

  • 訪問客服端,發起請求

服務端成功返回: java

客戶端識別到不一樣源,進行攔截:

4-1:Jsonp 的實現

客戶端:node

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="jsonp">jsonp</button>
  <script> const JSON_BUTTON = document.querySelector('#jsonp') JSON_BUTTON.onclick = () => { myJsonp('http://localhost:3000/test?type=1', 'jsonp', (body) => { console.log(body) }) } function myJsonp(url, jsonpName, callback) { if (url.includes('?')) { url += `&jsonpName=${jsonpName}` } else { url += `?jsonpName=${jsonpName}` } const script = document.createElement('script') script.src = url document.body.appendChild(script) document.body.removeChild(script) window[jsonpName] = (body) => { callback && callback(body) } } </script>
</body>
</html>

複製代碼

服務端: 同上json

客戶端成功拿到服務端的返回結果
複製代碼

注意:\color{red}{Jsonp 回調函數的名字不須要先後端約定,前端傳什麼,後端就返回什麼}後端

5:Jsonp 的缺點

只支持 get 方式,不支持其它的,不夠靈活跨域

6:解決跨域的其它辦法

  • 1:服務端代理(客戶端請求 A 服務器, A 服務器再去請求 B 服務器,A 服務器獲得結果以後在返回給客戶端)
  • 2:服務端使用第三方包(node 可使用 Cors)
  • 3: window.postMessage
  • 4: document.domain
  • 5: window.name

【學習筆記,有錯誤之處敬請指教,謝謝閱讀!😊】瀏覽器

相關文章
相關標籤/搜索