常見跨域解決辦法,及實現原理(一)----JSONP

1、什麼是跨域?

當協議、(主/子)域名、端口號中任意一個不相同時,都算做不一樣域,不一樣域之間互相請求資源,就稱爲跨域

一、什麼是同源策略及其限制內容?

同源策略是一種約定,是指 協議、域名、端口 三者相同,即使兩個不一樣域名指向同一個ip地址,也非同源。
image.pnghtml

限制內容:node

  • 同源策略限制內容有:Cookie、localStorage、IndexedDB
  • DOM 節點
  • AJAX請求
  • 但img、link、script這個三個標籤是容許跨域加載資源的

二、常見跨域場景

image.png

特別說明:ajax

  • 若是是由於協議和端口形成的跨域問題,「前臺」是無能爲力的。
  • 判斷是否跨域,僅僅是經過url對比來識別的,並不會對比url對應的ip地址。
  • 當咱們請求服務器數據出現跨域時,請求是能夠正常發出的,服務端也能夠收到返回結果,只是被瀏覽器攔截了請求結果。

跨域解決方法

一、jsonp(原理及優缺點以下)

  • 受同源策略的影響,ajax不容許進行跨域請求,但<script>標籤的src屬性沒有跨域限制,利用該特性,能夠從其餘跨域的url中獲取的json數據,
  • 優勢:JSONP兼容性較好,可用於解決主流瀏覽器的跨域問題,而且不須要XMLHttpRequest或ActiveX的支持,可以直接訪問響應文本,支持在瀏覽器與服務器之間雙向通訊。
  • 缺點:僅支持get方法,具備侷限性,存在安全問題,例如:被惡意注入代碼、遭受xss攻擊

二、JSONP實現過程

  • 在js中,咱們雖然不能直接用XMLHttpRequest請求不一樣域上的數據,但在頁面上引入不一樣域的js文件是能夠的,JSONP正是利用這一特性來實現的。
  • JSONP由兩部分組成:回調函數和數據,回調函數是當響應到來時,應該在頁面中調用的函數,而數據是傳入回調函數中的JSON數據。
  • 建立一個<script>標籤,將須要跨域訪問的接口地址賦值給src,並在該地址中向服務器傳遞函數名(可使用?callback=XX函數名),
  • 服務器接收到請求後,把傳遞進來的函數名和它須要給你的數據拼接成一個字符串,例如:傳遞進去的函數名是show,它準備好的數據是show('我不愛你')。
  • 最後服務器把準備的數據經過HTTP協議返回給客戶端,客戶端再調用執行以前聲明的回調函數(show),對返回的數據進行操做。

三、封裝實現

在開發中可能會遇到多個 JSONP 請求的回調函數名是相同的,這時候就須要本身封裝一個 JSONP函數。
首先建一個啓動本地服務的js,經過node啓動後,便可訪問http://localhost:3000本地服務
// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // Iloveyou
  console.log(callback) // show
  res.end(`${callback}('我不愛你')`)
})
app.listen(3000)
而後在index.html中的script標籤內運行該處代碼
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'Iloveyou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})