前端渣渣對requestAPI的不斷重構之路

彭道寬發表於2019-09-30

前言

? 感興趣的可以去看看阿寬的部落格

我還是自我介紹一下吧,本人 19屆畢業生,在大二的時候自學前端,沒有系統的學習,也沒人帶,從菜鳥教程那裡去學 html 、css 、js 等,然後中間去寫過一點 php,也是 CURD 的工作。

自學路上太艱難,因為不僅僅會遇到一些除了前端的問題,還會遇到許多其他沒涉及到的問題,那時候的自己屬於,這個東西能做出來就行了,不會去考慮優化,或者重構程式碼等,直到大三去實習了一段時間之後,才發現自己多菜,乃至現在畢業了,入部門直接做一些重要的需求,如 xx 展會演示的某個功能,看著前人程式碼,越感自己菜到極致。

前端渣渣對requestAPI的不斷重構之路

好了,不扯那麼多了,上邊是為了做鋪墊,因為這篇文章,會有我最初的程式碼風格和現在的一個風格。

功能

前端渣渣對requestAPI的不斷重構之路

這裡就以一個最簡單的功能進行講解,一個輸入框,輸入使用者名稱和密碼,然後點選登入,就登入完成啦 ~

登入成功之後,拿到使用者資訊 ~

最初的樣子

在我年少無知的時候,jQuery 就是爸爸,有他在,沒什麼做不到的,但是說實在話,那時候真的用 jQuery 就只是為了 ajax 傳送請求,於是我的程式碼是這樣的

// adapter.js
$.ajax({
  url: 'http://backend-dev-manage/login',
  method: 'post',
  dataType: 'json',
  data: {
    username: 'pengdaokuan',
    password: '123456'
  },
  success: function(data) {
    console.log(data)
  }
})
複製程式碼

?perfect ! 就很棒 ~

按道理來講,是沒得問題的,但是這時候,我身份就變了,就是...我成為了搬運工 ...

為什麼這麼說,因為每次要傳送請求,我都要 copy 程式碼,ctrl + cctrl + v 瞭解一下 ...

哪個頁面需要發請求,我直接一頓操作,copy 就完事了

// a.html
$.ajax({
  url: 'http://backend-dev-manage/getAllStudent',
  method: 'get',
  success: function(data) {
    console.log(data)
  }
})

// b.html
$.ajax({
  url: 'http://backend-dev-manage/getAllTeacher',
  method: 'get',
  success: function(data) {
    console.log(data)
  }
})

// c.html
$.ajax({
  url: 'http://backend-dev-manage/getAllManage',
  method: 'get',
  success: function(data) {
    console.log(data)
  }
})
複製程式碼

你沒看錯,我就是這麼操作的,一直到去年大三實習前,還是這種操作,但是!在看了一些別人程式碼之後,我,長大了...

前端渣渣對requestAPI的不斷重構之路

實習的成長

在實習的時候,看到上一個實習生寫的程式碼,我也試著改了一下。於是,程式碼成這樣了

// adapter.js
import $ from 'jquery'

export default function requestJQuery(url, method, data) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: url,
      method: method || 'GET',
      data: data,
      success: function(data) {
        resolve(data)
      },
      error: function(error) {
        reject(error)
      }
    })
  })
}
複製程式碼

在請求的地方我引入這個 requestJQuery 就完事了嘛,這樣就不用繼續 copy 了,我可真是個小機靈鬼

你以為這就完了嘛,不存在的,在我看了 ant-design-pro 對於 request 的這段程式碼之後,我枯了... 我果然還是菜啊...

前端渣渣對requestAPI的不斷重構之路

借鑑別人的程式碼

這時候從 jQuery 變成了 axios,於是程式碼成了這樣,90%借鑑 ant-design-pro

果粒橙有 5%果粒也叫果粒橙,我的程式碼中有 10%的 bug,這也是我的程式碼

// adapter.js

import axios from 'axios'

const codeMessage = {
  200: '伺服器成功返回請求的資料。',
  201: '新建或修改資料成功。',
  202: '一個請求已經進入後臺排隊(非同步任務)。',
  204: '刪除資料成功。',
  400: '發出的請求有錯誤,伺服器沒有進行新建或修改資料的操作。',
  401: '使用者沒有許可權(令牌、使用者名稱、密碼錯誤)。',
  403: '使用者得到授權,但是訪問是被禁止的。',
  404: '發出的請求針對的是不存在的記錄,伺服器沒有進行操作。',
  406: '請求的格式不可得。',
  410: '請求的資源被永久刪除,且不會再得到的。',
  422: '當建立一個物件時,發生一個驗證錯誤。',
  500: '伺服器發生錯誤,請檢查伺服器。',
  502: '閘道器錯誤。',
  503: '服務不可用,伺服器暫時過載或維護。',
  504: '閘道器超時。'
}

// 檢查http code
const checkStatus = response => {
  if (response.status >= 200 && response.status < 300) {
    return response
  }
  const errortext = codeMessage[response.status] || response.statusText
  // 彈窗通知報錯
  const error = new Error(errortext)
  error.name = response.status
  error.response = response
  throw error
}

/**
 * 封裝的請求函式
 * @param  {string} url
 * @param  {object} [option]
 * @return {object}
 */
export default function request(option) {
  const newOptions = Object.assign({}, option, {
    credentials: 'include'
  })
  
  if (
    newOptions.method === 'POST' ||
    newOptions.method === 'PUT' ||
    newOptions.method === 'DELETE'
  ) {
    if (!(newOptions.body instanceof FormData)) {
      newOptions.headers = {
        Accept: 'application/json',
        'Content-Type': 'application/json; charset=utf-8',
        ...newOptions.headers
      }
      newOptions.data = JSON.parse(JSON.stringify(newOptions.data))
    } else {
      newOptions.headers = {
        Accept: 'application/json',
        ...newOptions.headers
      }
    }
  } else {
    newOptions.headers = {
      Accept: 'application/json',
      ...newOptions.headers
    }
  }

  return axios(newOptions)
    .then(checkStatus)
    .then(response => {
      var res = response.data
      if (res.code === 1) {
        return res.data
      } else {
        Message.error({
          content: res.msg,
          duration: 1.5
        })
      }
    })
    .catch(err => {
      let status = err.name
      if (status === 401) {
        console.log('未經授權, 錯誤碼:', status)
      }
      if (status === 403) {
        console.log('禁止訪問, 錯誤碼:', status)
      }
      if (status <= 504 && status >= 500) {
        console.log('伺服器錯誤, 錯誤碼:', status)
      }
      if (status >= 404 && status < 422) {
        console.log('找不到資源路徑, 錯誤碼 :', status)
      }
    })
}
複製程式碼

“ ? 這個程式碼不錯,只是長得有點像 ant-design-pro ”

看到這,你以為完了嗎,no no no,上邊的程式碼還是太亂了,然後呢,我看了組裡的專案,對 request 的呼叫,又上升到了一個層級

前端渣渣對requestAPI的不斷重構之路

重構一下

最近看了組裡邊對 request 的處理, 覺得很用幫助, 最起碼對我來說, 又重新整理了我的看法, 於是借鑑了前人的程式碼, 再加上自己之前對 ant-design-pro 的理解, 重新寫了一遍, 並且將一些常量、方法抽了出去,看一下程式碼

/**
 * @param {String} actionName 請求的名稱
 * @param {Object} options
 * @param {Boolean} needAuthorToken 是否需要token,預設不需要
 * @param {Boolean} needCsrfToken 是否需要csrfToken,預設不需要
 */
import axios from 'axios'
import Cookies from 'js-cookie'
import { handleUrl, handleHttpStatus, handleResultStatus } from './utils'

class Adapter {
  // 獲取options
  getOptions = ({ options, needAuthorToken, needCsrfToken }) => {
    let { url, headers } = options
    // 檢查url,url可能為 /api/backend/,也可能是完整的url=http://seewo.com
    url = handleUrl(url) 
    if (needAuthorToken) {
      const authorToken = Cookies.get('x-auth-token') // 這個由你們定義
      headers['xauthtoken'] = authorToken
    }
    if (needCsrfToken) {
      const csrfToken = Cookies.get('csrfToken') // 這個由你們定義
      headers['x-csrf-token'] = csrfToken
    }
    return Object.assign(options, {
      url: url,
      method: options.method || 'GET',
      headers: headers
    })
  }

  // request
  dispatchCallAPI = ({ options, authorToken = false, csrfToken = false }) => {
    const options = this.getOptions({ options, authorToken, csrfToken })

    return axios(options)
      .then(handleHttpStatus)
      .then(handleResultStatus)
      .then(res => {
        /**
         * 如果返回的不是一個JSON物件,而是一個字串,因此需要對這個字串進行處理
         * 如果直接返回的是一個JSON物件,這個時候,JSON.parse會丟擲異常,如果出現異常
         * 我們直接返回這個物件本身的值即可
         */
        try {
          return JSON.parse(res.data)
        } catch (err) {
          return res.data
        }
      })
      .catch(error => {
        console.log(error)
      })
  }
}

export default new Adapter()
複製程式碼
import React from 'react'
import adapter from './adapter'

export class requestComponent extends React.PureComponent {
  componentWillMount() {
    // 傳送請求
    adapter
      .requestCallAPI({
        url: '/erek-vue-manage/user/retriveList',
        headers: { 'Content-Type': 'application/json; charset=utf-8' }
      })
      .then(res => {
        console.log(res)
      })
      .catch(err => {
        console.log(err)
      })
  }
}
複製程式碼

我不知道你們感覺如何,我是覺得比我一開始的程式碼,好看多了,而且逼格高了一些?

我將這個抽了出來, 寫在了 github 上, 如果感興趣的小夥伴可以去看一下哈 ~

傳送門 : AdapterAPI

主要功能

  • 通過 axios 進行封裝的統一請求 ✅
  • url進行判斷處理,✅
  • 進行了 http code 的狀態處理 ✅
  • 進行了後端返回的資料狀態碼處理 ✅

注意

? 程式碼不一定能直接使用,目的不是讓你直接搬過來用的

? 雖然可能我寫的都不比你的好 ~ 但是希望可以給你一些參考~

最後多說兩句話

掘金太多了人才了,我這邊只是記錄一下一些學習過程中的日常踩坑,或者程式碼的演變過程,當然,我相信有人天生就有計算機的天賦,我自認為我沒有,但是我喜歡敲程式碼,我相信勤能補拙,不要天天做一些反覆的 copy 工作就好了,望安好 ~

文章首發地址 : ? 阿寬的部落格

相關文章