前端廢物的自救之路(1)實現(xiàn)axios攔截器:對Nuxt.js中的@nuxtjs/axios進行封裝

前端廢物的自救之路(1)實現(xiàn)axios攔截器:對Nuxt.js中的@nuxtjs/axios進行封裝

前言

手頭有一個去年五月寫的遺留項目,以前使用的是Spring Boot+Thymeleaf+AmazeUI(前臺)+X-admin(后臺),由于用戶反映有許多需要改進的地方(并且打開項目一看,那代碼我自己也看不懂了,而且以現(xiàn)在的視角看那些代碼真的是爛到極致),故進行徹底重構(gòu)。

由于前臺需要SEO,并且考慮到上一版中使用AmazeUI+Thymeleaf開發(fā)的體驗極為痛苦,且新的后端(Gin)的模板引擎十分惡心(可能有人不這么認為,反正我認為Gin的模板語法比Thymeleaf惡心多了),于是前端選擇了Nuxt.js。并且,由于前臺對響應(yīng)式有需求,而且客戶認為Vuetify太花哨,iView基礎(chǔ)版的組件又不夠用,所以UI框架前后臺統(tǒng)一使用Ant Design Vue。

注:后端使用的是Gin,并且實現(xiàn)了DDDCQS。關(guān)于這個項目的后端我們過段時間再另行討論。但是為了解釋部分前端代碼,這一系列文章中可能也會貼出一些后端代碼以供參考。其中前端代碼會給出具體的文件名和路徑,后端代碼會在代碼前標識為“后端代碼”。

由于網(wǎng)絡(luò)上使用Nuxt.js+Ant Design Vue的文章實在是太少了,作為一個后端一天能寫600+行(以前用Java的時候一天最多甚至能寫1200+行),前端一天寫100行能工作的代碼就滿足了的前端廢物,為了讓以后再寫類似的項目的時候能不這么肉疼,于是決定記錄下本次開發(fā)的過程,不為別的,就為自己以后再用NuxtAnt Design Vue的時候少掉幾根頭發(fā)。

雖然這個項目不能開源,但是考慮到以后可能還會做類似的項目,并且類似的解決方案的Demo實在是太少了,所以在這個項目完結(jié)后我也會根據(jù)我的開發(fā)過程整理一個開源的種子項目發(fā)布在GitHub上以供參考和使用。

為什么需要axios攔截器?

項目本身是前后端分離的,鑒權(quán)使用的是JWT。由于后端是根據(jù)請求頭中的X-Token字段進行鑒權(quán)的,所以需要在每個請求的請求中放入Token。但是我們不可能在每個接口里都這么做,最佳的解決方案當然是利用axios攔截器來完成這個工作。

另外,后端所有的響應(yīng)無論成功還是失敗都具有統(tǒng)一的格式:

后端代碼

// @Description 生成一個成功請求的標準響應(yīng)格式的JSON
// @param data interface{} 響應(yīng)中要攜帶的數(shù)據(jù)
// @return result 生成的JSON
func Success(data interface{}) (result gin.H) {
    result = gin.H{
        "code":    Ok,
        "message": "Success",
        "data":    data,
    }
    return
}

// @Description 生成一個失敗請求的標準響應(yīng)格式的JSON
// @param errorCode ErrorCode 錯誤碼
// @param message string 要顯示給用戶的錯誤信息
// @return result 生成的JSON
func Error(errorCode ErrorCode, message string) (result gin.H) {
    result = gin.H{
        "code":    errorCode,
        "message": message,
        "data":    nil,
    }
    return
}

我們也不可能在每個接口里都去解析這個格式,這樣不僅不優(yōu)雅還很容易出錯,這時候就需要axios攔截器對響應(yīng)做統(tǒng)一的處理了。

封裝@nuxtjs/axios

由于Nuxt.jsaxios做了封裝,所以使用起來與直接使用axios有一些不同,在實現(xiàn)axios攔截器時與直接使用axios也不太一樣。

注:我在新建項目的時候就選擇了axios,所以我的項目創(chuàng)建出來是自帶@nuxtjs/axios這個依賴包的,如果沒有的話可以使用npm自行安裝。如果使用的是我這篇文章所講的方法,安裝時要安裝的包是@nuxtjs/axios而不是axios。

添加axios攔截器插件

與在Vue中配置axios攔截器不同,在Nuxt.js中配置axios攔截器使用的是Nuxt.js的插件機制。要配置axios攔截器,我們需要在@/plugins目錄下新建一個axios.js(名稱無所謂,只要是.js文件就可以了,但是該文件的名稱與配置文件中的名稱要對應(yīng))文件,在這個文件中進行具體的配置:

由于前端才剛剛開始寫,所以這個文件的內(nèi)容非常不全面,僅供參考,以后如果添加了新的重要的邏輯,會在后續(xù)文章中對這部分代碼進行編輯。

@/plugins/axios.js

import { message } from 'ant-design-vue'

export default function ({ store, redirect, app: { $axios } }) {
  // 后端接口地址
  $axios.defaults.baseURL = 'http://127.0.0.1:19090/api/'

  // Request攔截器:設(shè)置Token
  $axios.onRequest((config) => {
    // TODO 使用Vuex存儲Token,并做持久化處理
    // config.headers.common['X-Token'] = store.state.token
  })
  // Error攔截器:出現(xiàn)錯誤的時候被調(diào)用,根據(jù)狀態(tài)碼做對應(yīng)判斷并顯示全局Message
  $axios.onError((error) => {
    const code = parseInt(error.response && error.response.status)
    switch (code) {
      // 未登錄
      case 401:
        redirect('/login')
        break
      default:
        break
    }
    // 使用Ant Design Vue的message模塊顯示異常信息
    message.error(error.response.data.message, 5)
  })
  // Response攔截器:對正常返回的數(shù)據(jù)進行處理
  $axios.onResponse((response) => {
    return response.data
  })
}

$axios.onRequest$axios.onResponse$axios.onErrorNuxt.js提供的方法,類似于直接使用axios時的$axios.interceptors.response.use方法,可以在這些方法中完成對請求信息和響應(yīng)信息的判斷與處理。這些方法與直接使用axios最大的不同就是默認情況下不必返回任何內(nèi)容(當然,onResponse還是需要返回相應(yīng)數(shù)據(jù)response.data,也就是響應(yīng)體本身(JSON),包括了messagecode——當然如果不需要這兩部分的話也可以返回response.data.data),例如onRequest方法不必返回config等。

下面貼出純凈版的代碼供大家使用:

@/plugins/axios.js

export default function ({ store, redirect, app: { $axios } }) {
  $axios.onRequest((config) => {
  })
  
  $axios.onError((error) => {
  })
  
  $axios.onResponse((response) => {
  })
}

使用Ant Design Vue顯示全局消息

由于我在初始化項目的時候UI框架選擇了Ant Design Vue,所以項目里直接就整合好了Ant Design Vue,需要的時候直接引入就可以了——但是目前項目中沒有配置按需引入,所以前端性能可能會不太好,最后會配置一下按需引入。如果沒有選擇的話,可以使用npm進行安裝,再使用Nuxt插件進行整合。

可以看到我的代碼中有這么一句:

// 使用Ant Design Vue的message模塊顯示異常信息
message.error(error.response.data.message, 5)

因為對于錯誤信息,個人認為使用全局顯示的方法比組件內(nèi)單獨顯示更好,因為這樣可以更方便地做全局處理(當然也有人不這么認為,那樣的話可以在組件內(nèi)單獨使用Alert組件),所以每當請求出現(xiàn)錯誤的時候,都需要全局顯示這樣一條錯誤信息。與在組件內(nèi)顯示錯誤信息使用this.$message.error等不同,Ant Design Vue也給出了全局顯示錯誤信息的方法:

組件提供了一些靜態(tài)方法,使用方式和參數(shù)如下:

  • message.success(content, [duration], onClose)
  • message.error(content, [duration], onClose)
  • message.info(content, [duration], onClose)
  • message.warning(content, [duration], onClose)
  • message.warn(content, [duration], onClose) // alias of warning
  • message.loading(content, [duration], onClose)

——Ant Design Vue官方文檔

記得使用之前先導(dǎo)入message模塊:

import { message } from 'ant-design-vue'

效果:

image

在Nuxt.js中配置插件

有了插件之后還需要在Nuxt的配置文件nuxt.config.js中進行配置才能真正生效:

  // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
  plugins: [
    '@/plugins/antd-ui',
    '@/plugins/axios'
  ],

注意這里的文件名是剛剛在@/plugins目錄下新建.js文件時所使用的文件名。我的文件名是axios.js所以這里填寫@/plugins/axios,如果是axios-config.js就需要填寫@/plugins/axios-config,以此類推。

組件內(nèi)使用

一般方法

以登錄為例:

@/pages/login.vue

  methods: {
    login () {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.$axios.post('/login', {
            username: this.form.username,
            password: this.form.password
          }).then((res) => {
            // TODO 登錄后的Token保存、跳轉(zhuǎn)等操作
            this.$message.success(res.data.username, 5)
          })
        } else {
          return false
        }
      })
    },
    reset () {
      this.$refs.loginForm.resetFields()
    }
  }

與沒有axios攔截器的方法完全相同。

效果:

image

asyncData()方法

以獲取一個測試接口的信息為例,后端提供了一個測試接口/api/admin/test,攜帶Token進行請求的話會返回成功信息:

后端代碼

// TODO admin測試
adminGroup.GET("/test", func(c *gin.Context) {
        c.JSON(http.StatusOK, response.Success("/api/admin/test: Admin Test Succeeded!??"))
})

@/pages/admin目錄下新建一個測試用的頁面并在asyncData()方法中請求對應(yīng)數(shù)據(jù)(在請求之前需要設(shè)置一下Token。這里因為我還沒整合Vuex,直接寫死了,所以對應(yīng)的代碼沒什么參考價值,下一篇文章中會講如何整合Vuex以及在請求頭中設(shè)置Token):

@/pages/admin/test.vue

<template>
  <div>
    {{ info }}
  </div>
</template>

<script>
export default {
  name: 'AdminTest',
  asyncData (context) {
    return context.$axios.get('/admin/test').then((res) => {
      return {
        info: res
      }
    })
  },
  data () {
    return {
      info: ''
    }
  }
}
</script>

效果:

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容