从接口文档到前端调用:Axios 封装与实战全流程详解

文章目录

从接口文档到前端调用:Axios 封装与实战全流程详解

1.如何看接口文档?

在一个接口文档中,有很多的请求类型(也叫 HTTP 方法 / 动作)

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE

这里仅以 GET 和 POST 做讲解,其他的请求类型,都类似

1.1 GET

注意:GET 跟 URL 息息相关

来看一个具体的例子:

首先,接口都会有一个 baseUrl,作为获取数据的地址,使用不同的接口时,又会有不同的路径,比如这个就是/my/cate/info

第一部分:请求参数(Request Parameters)

Query 参数和 Header 参数

Query 参数和 Header 参数

query 跟 url 息息相关

header 跟身份验证相关,就是说,想让这个请求发送给接口,必须满足一些条件,header 就像一把钥匙

文档中这两部分都必须传对,否则接口要么报错,要么不给你数据。

  1. Query 参数
  • 出现在 URL 上。

  • 在这个接口里:

    复制代码
    GET /my/cate/info?id=1284

    这里的 id=1284 就是 文章分类的 id,告诉后台我要取哪一个分类的详情。

  • 所以:Query 参数就是用来指定查询条件的

  1. Header 参数
  • 放在 HTTP 请求的 请求头里。
  • 这里要求的参数是:
    • Authorization: Bearer <token>

Authorization:英语中授权的意思

  • 作用:用来验证用户身份(你是否已经登录)。
  • 也就是说:
    • 如果没有这个 token → 后台会返回 401 身份认证失败
    • 有正确 token → 才能拿到分类详情。

第二部分:响应(Response)

javascript 复制代码
{
  "code": 0,
  "message": "获取文章分类成功!",
  "data": {
    "id": 1070,
    "cate_name": "科技",
    "cate_alias": "keji"
  }
}
  • code :业务状态码。
    • 0 表示成功,1 表示异常。
  • message:提示消息(给人看的,比如"成功/失败原因")。
  • data :真正的业务数据,这里是文章分类的详情对象。
    • id:分类 ID。
    • cate_name:分类名称(比如 "科技")。
    • cate_alias:分类别名(比如 "keji")。

这部分就是告诉你:调用成功以后,后台会返回什么样的数据结构,你前端代码里就可以用这些字段来展示页面。

1.2 POST

看如下例子:返回响应部分不做介绍,POST 请求方法没有 Query 参数,而是 Body 参数

Body 参数(请求体)

这是 POST 接口的重点,和 GET 很不同:

  • 位置 :请求报文的 body 部分,不在 URL 上。
  • 格式 :这里文档写了 application/json,说明 body 要用 JSON 格式。
  • 作用 :用来传递 新增数据的内容

在文档里,它定义了两个字段:

  1. cate_name (string)
    • 文章分类名字,比如"娱乐"、"科技"。
    • 必填,长度限制:1--10 个非空字符。
  2. cate_alias (string)
    • 文章分类别名,比如"yule"、"keji"。
    • 必填,长度限制:1--15 个大小写字母和数字组合。

示例 JSON

复制代码
{
  "cate_name": "娱乐",
  "cate_alias": "yule"
}

为什么 GET 没有 Body,而 POST 要用 Body?

GET:只是获取数据,不修改,所以参数少,放 URL 上即可。

POST:要创建一条新数据,通常包含多个字段(标题、别名、内容......),如果写在 URL 会很长很乱,所以必须放到 Body 里,结构化成 JSON。

2.为什么要封装请求拦截器和响应拦截器?如何封装?

简单来说,实际开发过程中,不能每一次都手动的在请求头中加入授权的 token,而请求拦截器,会拦截请求将每个授权的 token 加入到本次的请求头当中.

统一封装 axios 请求模板:

官方文档:https://www.axios-http.cn/docs/interceptors

javascript 复制代码
import axios from 'axios'

const baseURL = 'http://big-event-vue-api-t.itheima.net'

const instance = axios.create({
  // 1. 基础地址,超时时间(配置项)
})

//请求拦截器
instance.interceptors.request.use(
  (config) => {
    // 2. 携带token,将 token 值赋给请求头
    return config
  },
  (err) => Promise.reject(err)
)

//响应拦截器
instance.interceptors.response.use(
//http 返回 200 成功
  (res) => {  
    // 3. 正确拿到了业务数据
    
    // 4. 业务逻辑错误,例如限制密码字母大写
    return res
  },
  (err) => {
    // 5. 处理http 返回401错误
    return Promise.reject(err)
  }
)

export default instance
  • 将封装写在 src/utils/request.js
  • 首先,创建一个 axios 实例
  • 其次,写出请求拦截器和响应拦截器

2.1 封装axios实例

(1)统一配置

  • 每个接口都要用到的公共参数,比如:
    • baseURL(基础路径)
    • timeout(超时时间)
    • headers(请求头,常常要放 token)
  • 如果不封装,每次 axios.get() / axios.post() 都要手动写,很麻烦。
  • 封装后 → 写一次,所有请求自动带上。

timeout:请求超过 x 毫秒 还没响应,就自动报错,进入 .catch()

请求中几乎所有接口都需要,而且不会频繁变化 的东西,适合直接写在 axios.create()headers 里,所以 token 不应该写在这里,token 应写在拦截器中

(2)更好的可维护性

  • 以后如果 baseURL 要改(比如从测试环境 → 正式环境),只需要改 request.js 这一处。
  • 如果不封装,你得在项目里到处找 URL,一个个修改,容易遗漏。

(3)添加请求/响应拦截器

(4)支持多实例

有些项目里:

  • 一部分接口走 https://api.example.com
  • 另一部分接口走 https://upload.example.com
    就可以创建两个不同的 axios 实例,互不影响。

封装axios实例代码如下:

javascript 复制代码
import axios from 'axios'
const baseURL = 'http://big-event-vue-api-t.itheima.net'

const instance = axios.create({
  baseURL,
  timeout: 100000
})

2.2 封装拦截器

官方文档中的拦截器

javascript 复制代码
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

分析:

request 拦截器的代码是一样的,关键在于拿到的对象不一样,一个是 config,一个是 response

  • config 对象
    • axios 会先把你传入的参数转成一个"配置对象",这个配置对象就是 config 对象
  • response 对象
    • 浏览器原生的 XHR 或 fetch 会产生一个响应结果,axios 会把这个结果"包装"成一个对象

注意:

  • 这里的 axios.替换成你对应的 axios 实例
  • function(config) 也可以替换成对应的箭头函数()=>{}

2.3 拦截器实践

请求拦截器:使用 pinia 存储的 token,将 token 值赋给请求头

javascript 复制代码
instance.interceptors.request.use(
  (config) => {
    const userStore = useUserStore()
    if (userStore.token) {
      config.headers.Authorization = userStore.token
    }
    return config
  },
  (err) => Promise.reject(err)

响应拦截器:

  • 成功拿到响应,直接返回,没拿到服务异常

  • 若错误,根据错误类型提示或跳转路由

javascript 复制代码
instance.interceptors.response.use(
  (res) => {
    if (res.data.code === 0) {
      return res
    }
    ElMessage({ message: res.data.message || '服务异常', type: 'error' })
    return Promise.reject(res.data)
  },
  (err) => {
    ElMessage({ message: err.response.data.message || '服务异常', type: 'error' })
    console.log(err)
    if (err.response?.status === 401) {
      router.push('/login')
    }
    return Promise.reject(err)
  }
)

导出

javascript 复制代码
export default instance
export { baseURL }

3.如何在实际的项目中使用axios

3.1 GET

无需请求参数的 GET

在 src/api 下创建自定义的 js 文件,如 article.js.存放跟文章相关的 api

src/api/article.js

  • 导入封装好的方法
javascript 复制代码
import request from '@/utils/request'
  • 实现 api
javascript 复制代码
//获取文章分类
export const artGetChannelsService=()=>request.get('/my/cate/list')
  • 在具体的.vue 文件中使用

    • 导入该方法

      javascript 复制代码
      import { artGetChannelsService} from '@/api/article'
    • 使用该方法

      javascript 复制代码
       const res =await artGetChannelsService()

需要请求参数的 GET

API 封装:

javascript 复制代码
// 获取文章分类详情 ?id=xxx
export const artGetChannelDetailService = (id) => {
  return request.get('/my/cate/info', {
    params: { id }          // => /my/cate/info?id=1284
  })
}

为什么写 params 呢?

因为能拼到 url 后面

在 .vue 里使用:

javascript 复制代码
import { artGetChannelDetailService } from '@/api/article'

const id = route.query.id   // 比如路由里带着 ?id=1284
await artGetChannelDetailService(id)

3.2 POST

以这个为例:

  • 导入封装好的方法
javascript 复制代码
import request from '@/utils/request'
  • 实现 api
javascript 复制代码
//添加文章分类
export const artAddChannelService=(data)=>request.post('/my/cate/add',data)

  • 在具体的.vue 文件中使用

    • 导入该方法

      javascript 复制代码
      import { artAddChannelService} from '@/api/article'
    • 使用该方法,此时注意了,POST 是需要我们传参的

      javascript 复制代码
      //定义表单数据
      const formModel=ref({
          cate_name:'',
          cate_alias:''
      })
      
      await artAddChannelService(formModel.value)

3.3. DELETE

Delete 也是需要一个请求参数,写法跟带参数的 GET 一样

API 封装:

javascript 复制代码
// 删除文章分类
export const artDelChannelService = (id) =>
  request.delete('/my/cate/del', {
    params: { id }
  })

3.4 PUT

put 跟 post 写法一样,除了 request 方法是 put

API 封装:

javascript 复制代码
export const artEditChannelService =(data)=> request.put('/my/cate/info',data)

在实际写法中,也可以不用形参 data,直接传对象,比如

javascript 复制代码
//更新个人信息
export const userUpdateInfoService = ({ id, nickname, email }) =>
  request.put('/my/userinfo', { id, nickname, email })

3.5 PATCH

API 封装:

跟 post 一样

javascript 复制代码
//上传头像
export const userUploadAvatarService = (avatar) => request.patch('/my/update/avatar', { avatar })
相关推荐
7澄11 小时前
Servlet 前后端交互实战(登录/注册案例)
运维·服务器·ajax·servlet·jquery·form·前后端交互
HIT_Weston1 小时前
46、【Ubuntu】【Gitlab】拉出内网 Web 服务:http.server 分析(三)
前端·ubuntu·gitlab
测试-鹏哥1 小时前
ITP平台全新Mock服务上线 —— 助力高效API测试
前端·python·测试工具
BlackWolfSky1 小时前
ES6 教程学习笔记
前端·javascript·es6
IT_陈寒1 小时前
Redis性能翻倍的5个冷门技巧:从缓存穿透到集群优化实战指南
前端·人工智能·后端
木易 士心1 小时前
Node.js 性能诊断利器 Clinic.js:原理剖析与实战指南
开发语言·javascript·node.js
不会写DN1 小时前
如何实现UniApp登录拦截?
前端·javascript·vue.js·typescript·uni-app·vue
不老刘1 小时前
深入理解 React Native 的 Metro
javascript·react native·react.js·metro
哆啦A梦15881 小时前
67 token 过期时间
前端·javascript·vue.js·node.js