1. 程式人生 > >vue中axios請求payload的暴力解決方案

vue中axios請求payload的暴力解決方案

  去年公司做了一個小程式商城專案,運營了一段時間決定再次開發一個H5商城,用於微信好友之間的分享以及app內的巢狀,於是便開了一個vue專案,介面大多都是複用小程式的。

  在做專案的過程中本人遇到一個由axios導致的問題——後臺介面拿不到請求的引數。因為介面都是之前寫好的,並且小程式執行正常,便沒有去讓後臺檢視,決定自己找出解決方案。開啟瀏覽器檢視引數發現axios的引數是這樣子的。

  首先我解釋下我們這個奇葩的引數。(其實網上有兩種解決方案,但是由於這個引數,那兩種方案都不能使用,我才不得不使用第三種暴力解決方案,下面會講到。)我們的這個介面的引數goodsId的值是一個json字串,該

json裡的key表示店鋪id,而值則是一個數組,數組裡面是商品id與數量的鍵值對。這種方法很奇葩,但是當時腦子抽了和後臺一拍板就這麼定了,現在想改也晚了。

  那麼想找出問題,我就要知道這個請求與小程式請求有什麼不同,於是開啟小程式,可以看到如下圖。

  在這兩次請求中,可以看出引數有明顯的不同,axios的引數是由resquest payload傳遞的在引數之上還有一層大括號。而小程式的則是由form data傳遞的。

  在請求中,決定引數形式的是標頭檔案中的content-type。再觀察我們可以看到兩者的content-type是不同的,前者是application/json,以json的方式請求,由於

json可以支援複雜的結構化資料,因此現在用的也越來越多,而後者是application/x-www-form-urlencoded是比較常見的請求方式,對key val 都進行了 URL 轉碼。

  知道了原因,就可以解決問題了。其實最方便的解決方式是與後臺溝通,讓他們相容一下。但是當時我們後臺忙於其他問題,這種可以前端解決的問題,就沒有去麻煩他們。那麼這種問題該如何解決呢。

一,更改params

  這種方式我們不更改content-type,直接對引數進行改變,將引數改成?id=id&name=name的形式放在url上。

function objToUrl(obj) {
  let str = '?'
  if(Object.prototype.toString.call(obj) === '[object Object]') {
    for (let key in obj) {
      if (key && obj.hasOwnProperty(key)) {
        str+=`${key}=${obj[key]}&`
        objToUrl(obj[key])
      }
    }
  }
  return str
}

export function fetchPost (url, params){
  let str = objToUrl(params)
  // let str = qs.stringify(params)
  return new Promise((resolve, reject) => {
    axios.post(url+str)
      .then(response => {
        resolve(response.data)
      })
      .catch(error => {
        reject(error)
      })
  })
  但是這種形式並不適合我們這種帶特殊符號的引數,在轉換過程中會出現轉義字元,並不能請求成功,因此我又去尋找第二種方式。

二 更改content-type

  這種方式是由開發者對axios進行封裝,首先更改axioscontent-type,改為我們熟悉的。

'Content-Type': 'application/x-www-form-urlencoded'

程式碼:axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'

假設你的引數為{‘bar’:’aaa’},那麼需要對引數進行一下處理:qs.stringify({‘bar’:’aaa’})

程式碼為:

export function fetchPost (url, params){
  params = qs.stringify(params)
  return new Promise((resolve, reject) => {
    axios.post(url,params,{
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      })
      .then(response => {
        resolve(response.data)
      })
      .catch(error => {
        reject(error)
      })
  })
}
  第二種方式滿足了我的需求,但是前幾天我再次踩到了axios的坑,因此一氣之下開始了第三種方案,暴力解決!

三 暴力解決方案

在後面的介面中,我們用到了一個比較複雜的json,其中包含了一個數組,這時使用第二種方案就出現了一種問題。


  如圖所示,qs在處理我的引數時,把陣列處理成了cartIds[index]: value的格式,這顯然不能讓後端成功接收。於是我便著手研究第三種方案。

 其實無論是axios還是之前的vue-resource,本質上都是ajax請求,而最最原始暴力的方式,便是用原生封裝一個ajax,無論外掛裡出了什麼么蛾子,都能平靜對待。於是便有了如下程式碼:

  // 暴力解決方案
export function fetchPost2 (url, params){
  return new Promise((resolve, reject) => {
    let xhr = new window.XMLHttpRequest()
    xhr.open('POST', url, true)
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    if(window.sessionStorage.getItem('setUserInfo')){
      let userInfo = JSON.parse(window.sessionStorage.getItem('setUserInfo'))
      xhr.setRequestHeader('Authentication', '')
      xhr.setRequestHeader('X-User', userInfo.id)
    }
    xhr.setRequestHeader('X-Channel', '')
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) { 
            resolve(JSON.parse(xhr.responseText))
        }else{
            // reject(error)
        }
    }
    xhr.send(params)
  })
}

是這裡的params也需要進行處理,處理成‘key=value&key=value’的形式,暫時解決了問題。(標頭檔案的設定是因為我們介面需要簽名)

事實證明原生可以解決一切問題~

(經過查閱發現,可以在qs中新增引數解決這個問題,如:params = qs.stringify(params,{ indices: false })。不過既然都用原生封裝了,就暫時這麼用了~當複習js基礎了)

四 後臺解決問題

  這種問題好像只出現在java後臺。因為我在做這個專案的時候,後臺已經寫好介面並忙於其他專案,就沒有去麻煩人家,但是從網上的一些文章看來,後臺處理也可以完美解決這個問題。

  如果出現這種狀況,說明後臺取引數是用@RequsetParam取引數的,讓後臺人員加上@RequestBody 註解就行了。

  由於本人對後臺不是很瞭解,在此不加拓展,如果你是在開發中遇到了這個問題,可以和java後臺商量解決,當然如果你遇到了我這樣的情況,也可以暴力解決。

(但axios封裝自有他的好處,優化的話是否是直接修改axios的原生程式碼更好呢?希望有大神告知更好的解決辦法。)