前端废物的自救之路(1)实现axios拦截器:对Nuxt.js中的@nuxtjs/axios进行封装

前端废物的自救之路(1)实现axios拦截器:对Nuxt.js中的@nuxtjs/axios进行封装

前言

手头有一个去年五月写的遗留项目,以前使用的是Spring Boot+Thymeleaf+AmazeUI(前台)+X-admin(后台),由于用户反映有许多需要改进的地方(并且打开项目一看,那代码我自己也看不懂了,而且以现在的视角看那些代码真的是烂到极致),故进行彻底重构。

由于前台需要SEO,并且考虑到上一版中使用AmazeUI+Thymeleaf开发的体验极为痛苦,且新的后端(Gin)的模板引擎十分恶心(可能有人不这么认为,反正我认为Gin的模板语法比Thymeleaf恶心多了),于是前端选择了Nuxt.js。并且,由于前台对响应式有需求,而且客户认为Vuetify太花哨,iView基础版的组件又不够用,所以UI框架前后台统一使用Ant Design Vue

注:后端使用的是Gin,并且实现了DDDCQS。关于这个项目的后端我们过段时间再另行讨论。但是为了解释部分前端代码,这一系列文章中可能也会贴出一些后端代码以供参考。其中前端代码会给出具体的文件名和路径,后端代码会在代码前标识为“后端代码”。

由于网络上使用Nuxt.js+Ant Design Vue的文章实在是太少了,作为一个后端一天能写600+行(以前用Java的时候一天最多甚至能写1200+行),前端一天写100行能工作的代码就满足了的前端废物,为了让以后再写类似的项目的时候能不这么肉疼,于是决定记录下本次开发的过程,不为别的,就为自己以后再用NuxtAnt Design Vue的时候少掉几根头发。

虽然这个项目不能开源,但是考虑到以后可能还会做类似的项目,并且类似的解决方案的Demo实在是太少了,所以在这个项目完结后我也会根据我的开发过程整理一个开源的种子项目发布在GitHub上以供参考和使用。

为什么需要axios拦截器?

项目本身是前后端分离的,鉴权使用的是JWT。由于后端是根据请求头中的X-Token字段进行鉴权的,所以需要在每个请求的请求中放入Token。但是我们不可能在每个接口里都这么做,最佳的解决方案当然是利用axios拦截器来完成这个工作。

另外,后端所有的响应无论成功还是失败都具有统一的格式:

后端代码

// @Description 生成一个成功请求的标准响应格式的JSON
// @param data interface{} 响应中要携带的数据
// @return result 生成的JSON
func Success(data interface{}) (result gin.H) {
    result = gin.H{
        "code":    Ok,
        "message": "Success",
        "data":    data,
    }
    return
}

// @Description 生成一个失败请求的标准响应格式的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
}

我们也不可能在每个接口里都去解析这个格式,这样不仅不优雅还很容易出错,这时候就需要axios拦截器对响应做统一的处理了。

封装@nuxtjs/axios

由于Nuxt.jsaxios做了封装,所以使用起来与直接使用axios有一些不同,在实现axios拦截器时与直接使用axios也不太一样。

注:我在新建项目的时候就选择了axios,所以我的项目创建出来是自带@nuxtjs/axios这个依赖包的,如果没有的话可以使用npm自行安装。如果使用的是我这篇文章所讲的方法,安装时要安装的包是@nuxtjs/axios而不是axios

添加axios拦截器插件

与在Vue中配置axios拦截器不同,在Nuxt.js中配置axios拦截器使用的是Nuxt.js的插件机制。要配置axios拦截器,我们需要在@/plugins目录下新建一个axios.js(名称无所谓,只要是.js文件就可以了,但是该文件的名称与配置文件中的名称要对应)文件,在这个文件中进行具体的配置:

由于前端才刚刚开始写,所以这个文件的内容非常不全面,仅供参考,以后如果添加了新的重要的逻辑,会在后续文章中对这部分代码进行编辑。

@/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拦截器:设置Token
  $axios.onRequest((config) => {
    // TODO 使用Vuex存储Token,并做持久化处理
    // config.headers.common['X-Token'] = store.state.token
  })
  // Error拦截器:出现错误的时候被调用,根据状态码做对应判断并显示全局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拦截器:对正常返回的数据进行处理
  $axios.onResponse((response) => {
    return response.data
  })
}

$axios.onRequest$axios.onResponse$axios.onErrorNuxt.js提供的方法,类似于直接使用axios时的$axios.interceptors.response.use方法,可以在这些方法中完成对请求信息和响应信息的判断与处理。这些方法与直接使用axios最大的不同就是默认情况下不必返回任何内容(当然,onResponse还是需要返回相应数据response.data,也就是响应体本身(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)

因为对于错误信息,个人认为使用全局显示的方法比组件内单独显示更好,因为这样可以更方便地做全局处理(当然也有人不这么认为,那样的话可以在组件内单独使用Alert组件),所以每当请求出现错误的时候,都需要全局显示这样一条错误信息。与在组件内显示错误信息使用this.$message.error等不同,Ant Design Vue也给出了全局显示错误信息的方法:

组件提供了一些静态方法,使用方式和参数如下:

  • 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官方文档

记得使用之前先导入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,以此类推。

组件内使用

一般方法

以登录为例:

@/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保存、跳转等操作
            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()方法中请求对应数据(在请求之前需要设置一下Token。这里因为我还没整合Vuex,直接写死了,所以对应的代码没什么参考价值,下一篇文章中会讲如何整合Vuex以及在请求头中设置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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容