POST請求變成OPTIONS的原理及處理

最近公司佈置了一個公衆號網頁項目,正好有段時間沒用Vue來搭建項目了,想一想仍是從Vue作起,正好如今腳手架也到3.0了,試試鋒利不鋒利。而後就遇到了很多坑。ios

Vue搭建完該請求接口調試,天然是用官方推薦的Axios了,而後就遇到了一個小問題,正常的Post請求,莫名其妙的變爲了OPTIONS請求。npm

HTTP請求方法

平時開發最多見最經常使用的HTTP請求應該是POST和GET。可是HTTP所提供的請求方法卻不止一種。axios

  • GET: 一般用於請求服務器發送某些資源
  • HEAD: 請求資源的頭部信息, 而且這些頭部與 HTTP GET 方法請求時返回的一致. - 該請求方法的一個使用場景是在下載一個大文件前先獲取其大小再決定是否要下載, 以此能夠節約帶寬資源
  • OPTIONS: 用於獲取目的資源所支持的通訊選項
  • POST: 發送數據給服務器
  • PUT: 用於新增資源或者使用請求中的有效負載替換目標資源的表現形式
  • DELETE: 用於刪除指定的資源
  • PATCH: 用於對資源進行部分修改
  • CONNECT: HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器
  • TRACE: 回顯服務器收到的請求,主要用於測試或診斷

至於這些方法具體的應用,這裏就不作論述了跨域

錯誤狀況


在代碼中明明是Post的請求,可是在谷歌控制面板中卻顯示的是OPTIONS,這是爲何? 瀏覽器

並且下面還有一段報錯。

問題根源

期初想着多是跨域的問題,而後讓後臺加了響應頭,可是問題並沒解決。後來根據英文提示請求違反CORS協議,去MDN上查詢了什麼事CORS。安全

翻閱MDN關於CORS介紹後(CORS介紹連接),在功能概述中後看到這樣一段話:bash

跨域資源共享標準(CORS:cross-origin sharing standard )新增了一組 HTTP首部字段,容許服務器聲明哪些源站經過瀏覽器有權限訪問哪些資源。另外,規範要求,對那些可能對服務器數據產生反作用的 HTTP 請求方法(特別是 GET 之外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否容許該跨域請求。服務器確認容許以後,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也能夠通知客戶端,是否須要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。CORS請求失敗會產生錯誤,可是爲了安全,在JavaScript代碼層面是沒法獲知到底具體是哪裏出了問題。你只能查看瀏覽器的控制檯以得知具體是哪裏出現了錯誤。服務器

意思就是對於跨域的請求,除了GET外或者搭配某些 MIME 類型的 POST 請求,都會先發送一次OPTIONS的預請求,用來檢測服務端是否容許該跨域請求。因此對於不符合規範的POST請求就會被擋到外面,因此纔會產生如上的報錯,只發送了OPTIONS請求,而重要的POST沒有發送。app

既然知道問題出在哪裏,那就須要讓發送的請求符合CORS協議。而根據報錯所給的提示 Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response. 結合控制面板上面請求頭的信息,發現Access-Control-Allow-Headers的content-type不符合要求,因此纔會觸發CORS檢測。post

解決方案

既然知道了請求被改變的緣由是由於觸發了CORS檢測,那麼只要符合CORS的規範,規避檢測,就能夠保證成功。在查閱資料後發現有以下內容,可以規避檢測。MDN原文以下(簡單請求):


某些請求不會觸發 CORS 預檢請求。本文稱這樣的請求爲「簡單請求」,請注意,該術語並不屬於 Fetch (其中定義了 CORS)規範。若請求知足全部下述條件,則該請求可視爲「簡單請求」:

  • 使用下列方法之一: GET HEAD POST
  • Fetch 規範定義了對 CORS 安全的首部字段集合,不得人爲設置該集合以外的其餘首部字段。該集合爲:
    1. Accept
    2. Accept-Language
    3. Content-Language
    4. Content-Type (須要注意額外的限制)
    5. DPR
    6. Downlink
    7. Save-Data
    8. Viewport-Width
    9. Width
  • Content-Type 的值僅限於下列三者之一:
    1. text/plain
    2. multipart/form-data
    3. application/x-www-form-urlencoded

請注意上面要求的是知足全部條件。而結合控制面板的信息,以及報錯信息,所發送的信息格式不屬於Content-Type中的任意一種。因此觸發了CORS檢測,致使POST請求不成功。那麼下面只須要使參數的格式是上面Content-Type中所要求的其中一種就能夠了。

驗證

而我當時所向後臺傳遞的是一個參數未經處理的Object對象,不屬於上面三種類型中的一種,因此解決方案就是對傳遞的參數進行處理知足要求。

  • 對參數進行字符串轉換

    根絕以往的開發經驗,對參數進行JSON.stringify()處理後,能夠看到請求已經能走通,可是傳遞給後臺的參數,並非後臺想要的。因此這樣做可能須要後臺的配合。 PS:上一家公司就是這樣處理參數的,因此這裏也是驗證猜想,沒想到確實有點用。

    能夠看到參數是字符串的形式。

  • 在 axios 中,可使用 URLSearchParams API

    var data = new URLSearchParams();
    data.append('id', '1');
    data.append('name', 'minmin');
    data.append('age', '23')
    axios.post('url, data).then( res => { ... } ) 複製代碼

    可是有一點不方便,若是參數特別多的話,這種方式費事費力,因此仍是用插件解決吧

  • 用插件解決,在項目中引用qs

    npm install --save  qs
    安裝不上的用淘寶鏡像,而後
    cnpm install --save qs 
    複製代碼
    //封裝請求方法,全部參數統一用qs.stringify(data)處理
    function httpRequest(url, method, data) {
        let rdata = { ...publicData, ...data }
        rdata = qs.stringify(rdata)
        if (method === "post") {
            return post(url, rdata)
        } else {
            return get(url, rdata)
        }
    }
    複製代碼

能夠在view source中看到參數已經被轉碼了。

結語

我的仍是建議在項目中使用插件解決吧,每次經過append參數費時又費力,固然若是公司用的是第一種向後臺傳參,不用插件也沒什麼問題。

相關文章
相關標籤/搜索