前端跨域問題解決方式

注: 文章摘自 寫Bug - 思否javascript

什麼是跨域

出於瀏覽器的同源策略限制。同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。能夠說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同源策略會阻止一個域的javascript腳本和另一個域的內容進行交互。所謂同源(即指在同一個域)就是兩個頁面具備相同的協議(protocol),主機(host)和端口號(port)html

解決方式

一. 設置代理

例: 在 vue.config.js 文件中添加以下配置前端

const webpack = require('webpack');

module.exports = {
  devServer: {
    open: true, // 在 DevServer 啓動, 且第一次構建完時自動打開網頁
    port: 8080, // 端口號
    proxy: { // 設置代理
      '/api': { // 網關地址
        target: 'https://test.com', // 接口的域名
        ws: true, // 啓用 websocket
        secure: false, // 若是是 https 協議,須要配置這個參數
        // 開啓代理:在本地會建立一個虛擬服務端,而後發送請求的數據,並同時接收請求的數據,
        // 這樣服務端和服務端進行數據的交互就不會有跨域問題
        changOrigin: true, // 跨域需開啓
        pathRewrite: { // 重寫地址
          '^/api': '' // 將前綴 '/api' 轉爲 '/'
        }
      }
    }
  }
}
複製代碼

二. JSONP

注: 因爲本質上 script 加載資源的方式是GET, 因此 JSONP 只能用於 GET 請求vue

在HTML標籤裏,一些標籤好比 script、img 這樣的獲取資源的標籤是沒有跨域限制的,利用這一點,咱們能夠這樣幹:java

  • 後端寫個小接口
// 處理成功失敗返回格式的工具
const {successBody} = require('../utli')
class CrossDomain {
  static async jsonp (ctx) {
    // 前端傳過來的參數
    const query = ctx.request.query
    // 設置一個cookies
    ctx.cookies.set('tokenId', '1')
    // query.cb是先後端約定的方法名字,其實就是後端返回一個直接執行的方法給前端,因爲前端是用script標籤發起的請求,因此返回了這個方法後至關於立馬執行,而且把要返回的數據放在方法的參數裏。
    ctx.body = `${query.cb}(${JSON.stringify(successBody({msg: query.msg}, 'success'))})`
  }
}
module.exports = CrossDomain;
複製代碼
  • 前端這樣寫
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script type='text/javascript'>
      // 後端返回直接執行的方法,至關於執行這個方法,因爲後端把返回的數據放在方法的參數裏,因此這裏能拿到res。
      window.jsonpCb = function (res) {
        console.log(res)
      }
    </script>
    <script src='http://localhost:9871/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>
  </body>
</html>
複製代碼
  • 簡單封裝一下前端這個套路
const request = ({url, data}) => {
  return new Promise((resolve, reject) => {
    // 處理傳參成xx=yy&aa=bb的形式
    const handleData = (data) => {
      const keys = Object.keys(data)
      const keysLen = keys.length
      return keys.reduce((pre, cur, index) => {
        const value = data[cur]
        const flag = index !== keysLen - 1 ? '&' : ''
        return `${pre}${cur}=${value}${flag}`
      }, '')
    }
    // 動態建立script標籤
    const script = document.createElement('script')
    // 接口返回的數據獲取
    window.jsonpCb = (res) => {
      document.body.removeChild(script)
      delete window.jsonpCb
      resolve(res)
    }
    script.src = `${url}?${handleData(data)}&cb=jsonpCb`
    document.body.appendChild(script)
  })
}
// 使用方式
request({
  url: 'http://localhost:9871/api/jsonp',
  data: {
    // 傳參
    msg: 'helloJsonp'
  }
}).then(res => {
  console.log(res)
});
複製代碼

三. 空 iframe 加 form

  • 後端寫個小接口
// 處理成功失敗返回格式的工具
const {successBody} = require('../utli')
class CrossDomain {
  static async iframePost (ctx) {
    let postData = ctx.request.body
    console.log(postData)
    ctx.body = successBody({postData: postData}, 'success')
  }
}
module.exports = CrossDomain;
複製代碼
  • 前端這樣寫
const requestPost = ({url, data}) => {
  // 首先建立一個用來發送數據的iframe.
  const iframe = document.createElement('iframe')
  iframe.name = 'iframePost'
  iframe.style.display = 'none'
  document.body.appendChild(iframe)
  const form = document.createElement('form')
  const node = document.createElement('input')
  // 註冊iframe的load事件處理程序,若是你須要在響應返回時執行一些操做的話.
  iframe.addEventListener('load', function () {
    console.log('post success')
  })

  form.action = url
  // 在指定的iframe中執行form
  form.target = iframe.name
  form.method = 'post'
  for (let name in data) {
    node.name = name
    node.value = data[name].toString()
    form.appendChild(node.cloneNode())
  }
  // 表單元素須要添加到主文檔中.
  form.style.display = 'none'
  document.body.appendChild(form)
  form.submit()

  // 表單提交後,就能夠刪除這個表單,不影響下次的數據發送.
  document.body.removeChild(form)
}
// 使用方式
requestPost({
  url: 'http://localhost:9871/api/iframePost',
  data: {
    msg: 'helloIframePost'
  }
});
複製代碼

四. CORS

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)跨域資源共享 CORS 詳解。看名字就知道這是處理跨域問題的標準作法。CORS有兩種請求,簡單請求和非簡單請求。node

只要同時知足如下兩大條件,就屬於簡單請求。webpack

  • 請求方法是如下三種方法之一:web

    • HEAD
    • GET
    • POST
  • HTTP的頭信息不超出如下幾種字段:json

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
  • 後端segmentfault

// 處理成功失敗返回格式的工具
const {successBody} = require('../utli')
class CrossDomain {
  static async cors (ctx) {
    const query = ctx.request.query
    // *時cookie不會在http請求中帶上
    ctx.set('Access-Control-Allow-Origin', '*')
    ctx.cookies.set('tokenId', '2')
    ctx.body = successBody({msg: query.msg}, 'success')
  }
}
module.exports = CrossDomain;
複製代碼

前端什麼也不用幹,就是正常發請求就能夠,若是須要帶cookie的話,先後端都要設置一下

fetch(`http://localhost:9871/api/cors?msg=helloCors`, {
  // 須要帶上cookie
  credentials: 'include',
  // 這裏添加額外的headers來觸發非簡單請求
  headers: {
    't': 'extra headers'
  }
}).then(res => {
  console.log(res)
});
複製代碼
相關文章
相關標籤/搜索