axios 跨域請求詳解

寫這篇文章的背景是由於以前遇到的,在跨域的狀況下經過 axios 發起的 get 請求正常,post 請求會在正式請求發送以前先發送一個 opstions 請求,然後端接口沒有兼容 options,致使 404 的狀況。前端

而在解決這個問題時帶着好奇心順帶查了一下,給本身補充了些知識點。ios

跨域請求分兩種

簡單講,從 JavaScript 代碼發起的 XMLHttpRequest 請求能夠分爲兩種:json

簡單請求:

不會觸發CORS預檢的請求,而是直接向服務端發送請求,什麼是 CORS預檢 我們後面說,其匹配的規則大體以下:axios

  1. 請求方法爲 GETHEADPOST 中的一種
  2. CORS安所有首字段在如下集合中:
    • ACCEPT
    • Accept-Language
    • Content-Language
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
    • Content-Type (值僅限text/plain,multipart/form-data,application/x-www-form-urlencoded)
  3. 請求中的 XMLHttpRequestUpload 對象沒有註冊任何事件監聽
  4. 請求中沒有 ReadableStream 對象

預檢請求:

在發送正式請求以前,會先發起一個 OPTIONS 預檢請求到服務器,以獲知服務器是否容許該實際請求,若不容許,則再也不發送請求,其匹配規則以下:後端

  1. 請求方法爲:PUTDELETECONNECTOPTIONSTRACEPATCH 之一
  2. 人爲設置了 CORS安所有首字段集合 以外的字段
  3. 請求中的 XMLHttpRequestUpload 對象註冊了任意事件監聽器
  4. 請求中使用了 ReadableStream 對象

在跨域請求中,若服務端返回了正確的跨域響應部首:Access-Control-Allow-OriginAccess-Control-Allow-MethodAccess-Control-Allow-Headers, 則跨域請求能正常獲取數據。api

問題解決

根據以上了解的知識點,跟進遇到的問題,發現 axios 的請求部首 Content-Type 的值默認爲 application/json;charset=utf-8,且 POST 請求數據爲 json 格式,故進行 POST 請求會先發出預檢請求,若服務端對預檢請求的響應爲不支持,則請求終止。跨域

根據上面分析出的緣由,如下列舉兩種解決方案:安全

完善服務端接口及跨域響應部首

使服務端接口支持 OPTIONS 方法,且對跨域預檢請求的請求部首進行完整的響應匹配,代表服務器將接受後續的實際請求,則實際請求將被正常響應。服務器

前端側處理 axios 的 POST 請求數據

跨域時將請求轉換爲簡單請求:cookie

  1. 請求部首的 Content-Type 設爲 application/x-www-form-urlencoded
  2. 處理 POST 請求數據,方式有如下兩種:
    • 經過 URLSearchParams 生成POST 請求的數據
    • 使用 qs 庫的 stringify api 對請求數據進行轉換(若請求數據中某個字段的值爲引用類型,須要先經過 JSON.stringify 處理,以防止服務端沒法識別

例子:

/* 經過 qs 模塊處理請求數據*/
import axios from 'axios'
import qs from 'qs'

axios.defaults.withCredentials = true // 若跨域請求須要帶 cookie 身份識別
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

// 請求攔截器
axios.interceptors.request.use(req => {
    // 對 post 請求數據進行處理
    if (req.method === 'post') {
        Object.keys(req.data).forEach(item => {
            !isPrimeval(req.data[item]) && (req.data[item] = JSON.stringify(req.data[item]))
        })
        req.data = qs.stringify(req.data)
    }
    return req
}, error => {
    // 請求出錯時處理
    return Promise.reject(error)
})

or

/* 經過 URLSearchParams 生成 POST 請求數據 */
import axios from 'axios'

async function anInterface (url, params = {}) {
    let data = new URLSearchParams()
    for(let key in params) {
        data.append(params[key])
    }
    const res = await axios.post(url, data)

    // 處理數據
    
    return res.data
}
複製代碼

經過以上方式便可將 POST 預檢請求轉換爲簡單請求,其好處不言而喻,對於多個 POST 請求而言,能夠減小一半的請求數量,且在一些服務端比較不能改動的場景更爲適用。

以上爲本次文章全部內容,如有問題,望指正;若需轉載,望註明出處

相關文章
相關標籤/搜索