axios 二次封装 api的统筹管理 配合async await实际项目中的运用

写在前面


作者简书地址
axios在实战项目中的运用,所举例项目是基于vue全家桶(vue-router+vuex+axios+element-ui)的后台管理系统,需要一些有vue项目开发经验的读者阅读。
由于vue-resource 作者宣布不再更新,促使我们使用第三方的request数据请求,一般我们的选择有

  1. JQuery ajax
  • 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
  • 基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
  • JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
  1. fetch
  • 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
    更好更方便的写法
  • 更加底层,提供的API丰富(request, response)
  • 脱离了XHR,是ES规范里新的实现方式
    1)fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
    2)fetch默认不会带cookie,需要添加配置项
    3)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
    4)fetch没有办法原生监测请求的进度,而XHR可以
  1. axios
  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止CSRF/XSRF

题外话(一)

一般在项目中我们会常常遇到跨域的问题,主要受同源策略(协议,路径,端口)的影响
尤其是使用vue-cli这种脚手架工具开发时,由于项目本身启动本地服务是需要占用一个端口的,所以必然会产生跨域的问题。
在vue-cli项目中webpack的proxyTable帮我们很好的处理了跨域问题:


image.png

如何配置:在项目生成的config目录下的index.js找
有些项目可能要接不同域名下的接口,而proxyTable支持配置多个域名,有这种需求的小伙伴可以自行配置。

划下重点------>
axios的请求会在你的url路径上自动拼接你的target(前提是我这种管理api的方式,后面会提)
记住我画的那个部分(记住这个配置)

 proxyTable: {
      '/api/**': {
        target: 'https://www.jianshu.com:8080/', //==>你的目标域名和端口
        changeOrigin: true,
        pathRewrite: {
          '^/': '/'
        }
      },
    }

主题开始

参考axios-中文使用手册:https://www.kancloud.cn/yunye/axios/234845

一、在我们接Api的时候 主要有两个阶段

  • request 请求阶段 ===》对应了axios.interceptors.request 请求拦截器过滤请求
  • response 响应阶段 ===》对应了axios.interceptors.response 响应拦截器过滤响应
    我一般在项目中建一个API文件夹 统一管理api 以及 axios的request的封装方法 目录为下


    image.png

下面直接上代码了:

import axios from 'axios'
import { throwErr } from '@/utils' //utils 捕捉服务端http状态码的方法
import store from '@/store'   //引入vuex的相关操作
import { Message } from 'element-ui' //element Toast的提示
import router from '@/router'

//过滤请求
axios.interceptors.request.use(config => {
  //config 为请求的一些配置 例如:请求头 请求时间 Token  可以根据自己的项目需求个性化配置,参考axios的中文说明手册  自己多动动手
 //由于我们项目的后端大大给力,很多东西在服务端帮我们处理好了所以请求阶段只要传好参数就好了
  config.timeout = 10 * 1000 //请求响应时间
  return config
}, error => {
  return Promise.reject(error)
})
// 添加响应拦截器
axios.interceptors.response.use(
  response => {
    if (response.data.code === 0) {   //服务端定义的响应code码为0时请求成功
      return Promise.resolve(response.data) //使用Promise.resolve 正常响应
    } else if (response.data.code === 1401) { //服务端定义的响应code码为1401时为未登录
      store.dispatch('setUserInfo', {})
      Message({
        message: '未登录'
      })
      // router.push('/login')
      return Promise.reject(response.data)    //使用Promise.reject 抛出错误和异常
    } else {
      return Promise.reject(response.data)
    }
  },
  error => {
    if (error && error.response) {
      let res = {}
      res.code = error.response.status
      res.msg = throwErr(error.response.status, error.response) //throwErr 捕捉服务端的http状态码 定义在utils工具类的方法
      return Promise.reject(res)
    }
    return Promise.reject(error)
  }
)
export default function request(method, url, data) {  //暴露 request 给我们好API 管理
  method = method.toLocaleLowerCase()   //封装RESTful API的各种请求方式 以 post get delete为例
  if (method === 'post') {
    return axios.post(url, data)    //axios的post 默认转化为json格式
  } else if (method === 'get') {     
    return axios.get(url, {
      params: data
    })
  } else if (method === 'delete') {
    return axios.delete(url, {
      params: data
    })
  }
}

以下为throwErr的源码

//axios捕错
export const throwErr = (code, response) => {
  let message = '请求错误'
  switch (code) {
    case 400:
      message = '请求错误'
      break
    case 401:
      message = '未授权,请登录'
      break
    case 403:
      message = '拒绝访问'
      break
    case 404:
      message = `请求地址出错: ${response.config.url}`
      break
    case 408:
      message = '请求超时'
      break
    case 500:
      message = '服务器内部错误'
      break
    case 501:
      message = '服务未实现'
      break
    case 502:
      message = '网关错误'
      break
    case 503:
      message = '服务不可用'
      break
    case 504:
      message = '网关超时'
      break
    case 505:
      message = 'HTTP版本不受支持'
      break
    default:
  }
  return message
}

如果有的小伙伴 是'application/x-www-form-urlencoded'为请求格式的话
可以使用 npm i qs -S 使用qs包将参数序列化

  transformRequest:[function(data){
      //在这里根据自己的需求改变数据
      return qs.stringify(data,{arrayFormat:'repeat'})
    }],

二 在api管理文件夹中如何定义接口呢?

以account.js为例

  • 引入request.js
  • 暴露你定义的api接口
  • request 的三个参数(mtehod,url,params)
    1. method 及axios的请求方法 (post,get,delete ···Ï)
    2. url 服务端的接口路径
      3.params 接口所传递的参数 全部以对象的形式传入 (后面会提 不着急)
image.png
import request from './request' //引入axios的封装方法

export const getAdminList = (params) => {
 return request('get', '/api/v1.0/admin/list', params) //登陆管理员获取自身信息
}

export const register = (params) => {
 return request('post', '/api/v1.0/admin/register', params) //添加管理员
}

export const deleteAdmin = (id, params) => {
 return request('delete', `/api/v1.0/admin/${id}`, params) //更新管理员信息
}

是不是很简单 管理的接口就是这么简洁帅气 为我打个call

在项目中如何使用定义的接口?

1.在页面中import对应的模块api文件以及其中的接口
2.不管是哪种请求 我们全部用对象(键值对)的形式传参数
3.用async 和await 在methods中定义接口方法

  • 用来区分我们所定义的普通方法
  • 可以方便我们处理函数回调 用同步的思想写代码 方便理解
  • 不用.then那种链式写法 美化代码 增加可读性和理解性
  • 配合try catch用来捕错
image.png

此例子可以在我的上篇关于 vue mixin的文章结合观看 观码效果更加
传送门

 getParams() {
      return {
        role: this.role,
        name: this.name,
        status: this.status,
        start: (this.PAGINATION.currentPage - 1) * this.PAGINATION.pageSize,
        range: this.PAGINATION.pageSize,
      }
    },
    async getAdminList() {
      this.loading = true
      let res = await getAdminList(this.queryParams)
      this.tableData = res.data.list
      this.PAGINATION.total = res.data.count
      this.loading = false
    },
  async deleteAdmin() {
      try {
        let res = await deleteAdmin(this.id)
        this.$message({
          type: 'success',
          message: '删除成功'
        })
        this.getAdminList()
      } catch (err) {
        this.$message({
          type: 'error',
          message: err.msg
        })
      }
    },
 addParams() {
      let { role, username, name, password, gender, profession, education, signature } = this.form
      return { role, username, name, password, gender, profession, education, signature }
    },
 async register() {
      try {
        let res = await register({ ...this.addParams() })
        this.$message({
          type: 'success',
          message: '添加成功'
        })
        this.dialogFormVisible = false
        this.$emit('listen')
      } catch (err) {
        this.$message({
          type: 'error',
          message: err.msg
        })
      }
    },

在实际表现如下
1.get 操作表现


image.png

2.delete操作表现


image.png

3.post 操作


image.png

题外话(二)

关于RESTful API的理解 (主要是后端开发者观看,前端小伙伴了解即可)

精简点:

  • http的动词(get post delete put patch ···)描述操作
  • url地址定位资源

RESTful API:

这是一种设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端与服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层级,更易于实现缓存等机制。在这种风格中,每个url路径代表一种资源(resource),所以路径中不推荐有名词,而且所用的名词往往与数据库的表格名对应,且一般采取复数的形式命名。而对应资源的具体操作类型,而由HTTP动词表示,即 GET/POST/PUT/PATCH/DELETE

image.png

这是我在看书过程中所阅读到的,具体书名就不透露了,免得有打广告嫌疑

写在后面

以上就是axios的二次封装如何在项目中如何优雅的使用,统一使用对象传参的方式。我觉得这种方式很方便优雅,因此将它分享出来,希望大家会喜欢。觉得有用的小伙伴可以给个心,给个关注,有不懂的地方可以评论我和私信我,有空会一一解答。下次会分享一篇在vue中如何将tinyMce封装成组件,tinyMCe富文本编辑器如何上传图片和文件。
简书首发网址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容