1. 封装的目的

在这里插入图片描述

2.基础准备

pnpm add axios
# 若需要处理 TypeScript 类型(可选),额外安装
pnpm add @types/axios -D

3.封装通用请求实例(仅参考)

// 引入 axios
import axios from 'axios'

// 1. 创建 axios 实例,配置基础参数
const service = axios.create({
  // 基础请求地址:区分开发/生产环境(关键!避免打包后修改地址)
  baseURL: process.env.NODE_ENV === 'development' 
    ? 'http://localhost:8080/api'  // 开发环境(后端本地/测试服务器地址)
    : 'https://prod-api.xxx.com/api', // 生产环境(线上服务器地址)
  // 请求超时时间:超过该时间未响应,视为请求失败
  timeout: 10000,
  // 统一请求头(可选,根据后端要求配置)
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
})

// 2. 请求拦截器:发送请求前做的处理(如添加 token、修改请求参数格式)
service.interceptors.request.use(
  (config) => {
    // 【常见场景 1】添加用户登录 token(后端需要身份验证时)
    // 从 uni-app 本地存储中获取 token(登录后存入)
    const token = uni.getStorageSync('userToken')
    if (token) {
      // 给请求头添加 token,字段名(Authorization)按后端要求修改(如 token、X-Token)
      config.headers.Authorization = `Bearer ${token}`
    }

    // 【常见场景 2】如果是 GET 请求,统一处理参数格式(可选)
    // if (config.method === 'get') {
    //   config.params = { ...config.params } // 可添加额外公共参数,如 timestamp: Date.now()
    // }

    // 【常见场景 3】如果是上传文件(FormData 格式),修改请求头
    // if (config.data instanceof FormData) {
    //   config.headers['Content-Type'] = 'multipart/form-data'
    // }

    return config
  },
  (error) => {
    // 请求发送失败(如网络中断、配置错误)的处理
    console.error('请求发送失败:', error)
    return Promise.reject(error)
  }
)

// 3. 响应拦截器:收到后端响应后做的处理(如统一解析数据、处理错误码)
service.interceptors.response.use(
  (response) => {
    // 取出后端返回的核心数据(一般后端会返回 { code: 200, data: {}, msg: '成功' } 格式)
    const res = response.data

    // 【常见场景 1】统一处理成功响应:只返回核心 data,简化接口调用
    if (res.code === 200 || res.code === 0) { // 后端成功码按实际约定修改(200/0 最常用)
      return res.data // 后续接口调用时,直接拿到 data 数据,无需再解构
    }

    // 【常见场景 2】统一处理业务错误(如参数错误、token 过期、无权限)
    else {
      // 1. 弹出错误提示(uni-app 内置提示框)
      uni.showToast({
        title: res.msg || '请求失败',
        icon: 'none',
        duration: 2000
      })

      // 2. 特殊错误码单独处理(如 token 过期,跳转登录页)
      if (res.code === 401) { // 401 一般代表未登录/token 过期
        uni.showModal({
          title: '提示',
          content: '登录状态已过期,请重新登录',
          showCancel: false,
          success: () => {
            // 清除本地 token,跳转登录页
            uni.removeStorageSync('userToken')
            uni.redirectTo({ url: '/pages/login/login' })
          }
        })
      }

      // 3. 把错误抛出去,便于接口调用时单独处理(可选)
      return Promise.reject(new Error(res.msg || '业务请求失败'))
    }
  },
  (error) => {
    // 【常见场景 3】处理网络错误/服务器错误(如 404、500、请求超时)
    console.error('响应处理失败:', error)
    const errMsg = error.message || '网络异常,请稍后重试'
    
    // 弹出错误提示
    uni.showToast({
      title: errMsg,
      icon: 'none',
      duration: 2000
    })

    return Promise.reject(error)
  }
)

// 4. 导出封装好的 axios 实例,供后续接口调用
export default service

4.api封装接口在不同情况下如何写

基于上面封装的 request 实例,按业务模块拆分接口(如用户模块、商品模块),每个接口对应后端的一个接口地址。
只需配置「请求方法、接口路径、请求参数」即可。

参数传递

  1. GET请求用params:参数自动拼在URL后,适合少量查询参数(如果是 uni 原生中的 data,GET 请求时,uni.request 会自动将 data 拼接为 URL 参数)
  2. POST/PUT请求用data:参数放在请求体中,适合传递大量提交/更新数据(data直接传递无需解包)
  3. 路径参数(如/{id}):直接用反引号拼写在url中,路径参数的字段名和顺序必须和后端接口保持一致,否则会请求失败(如后端要求 /user/{userId}/order/{orderNo}不能写成 /user/{orderNo}/order/{userId})

每个接口返回 request(…)/http(…):本质是返回一个 Promise 对象,后续调用时可使用 async/await 或 .then()
处理。

统一步骤

// 引入封装好的 axios 实例
import request(http) from '@/utils/request'

第一种方法

// 【用户模块接口】

// 场景 1:GET 请求(查询数据,参数拼在 URL 上)
// 示例:获取用户信息(后端接口:GET /user/info,参数:userId(可选,从 token 解析也可))
export const getUserInfo = (userId) => {
  return request({
    method: 'GET',
    url: '/user/info', // 接口路径(拼接在 baseURL 后面,完整地址:baseURL + /user/info
    params: { userId } // GET 请求参数:会自动拼在 URL 后,格式:?userId=xxx
  })
}

//uniapp原生
// 【用户模块】获取用户信息(GET 请求,对应 axios 的 get 接口)
export const getUserInfo = (userId) => {
  return http({
    method: 'get',
    url: '/user/info',
    data: { userId } // 对应 axios 的 params,自动拼接为 URL 参数:?userId=xxx
  })
}

第二种方法

// 场景 2:POST 请求(提交数据,参数放在请求体中)
// 示例:用户登录(后端接口:POST /user/login,参数:username、password)
export const userLogin = (data) => {
  return request({
    method: 'POST',
    url: '/user/login',
    data: data // POST 请求参数:放在请求体中,格式为 JSON(对应 request.js 中的 Content-Type)
    //(data直接传递无需解包)
  })
}
// 这是写在组件中,在api中不用管
// 页面调用时,直接传对象即可
await userLogin({ username: 'admin', password: '123456' })

//uniapp原生
// 【用户模块】登录接口(POST 请求,对应 axios 的 post 接口)
export const userLogin = (loginData) => {
  return http({
    method: 'post', // 和 axios 一致,小写即可
    url: '/user/login', // 会自动拼接 baseURL
    header: {
      // 对应 axios 的 headers,注意是单数!
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    data: loginData // 对应 axios 的 data,请求体参数,直接传递对象即可
  })
}

在这里插入图片描述

第三种方法

// 场景 3:PUT 请求(更新数据,全量更新)
// 示例:修改用户昵称(后端接口:PUT /user/nickname,参数:userId、nickname)
export const updateUserNickname = (data) => {
  return request({
    method: 'PUT',
    url: '/user/nickname',
    data: data
  })
}

第四种方法

// 场景 4:DELETE 请求(删除数据)
// 示例:删除用户地址(后端接口:DELETE /user/address/{addressId},参数:addressId(路径参数))
export const deleteUserAddress = (addressId) => {
  return request({
    method: 'DELETE',
    url: `/user/address/${addressId}` // 路径参数:直接拼接在 URL 中(注意用反引号 ``)
  })
}

在这里插入图片描述

第五种方法


// 场景 5:上传文件(FormData 格式,需配合 request.js 中的请求头修改)
// 示例:上传用户头像(后端接口:POST /user/avatar)
export const uploadUserAvatar = (file) => {
  // 创建 FormData 实例
  const formData = new FormData()
  // 添加文件(字段名 'avatar' 按后端要求修改)
  formData.append('avatar', file)
  // 发起请求
  return request({
    method: 'POST',
    url: '/user/avatar',
    data: formData,
    // 覆盖默认请求头(对应 request.js 中的注释场景 3)
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
}

5. 在 页面/组件中调用接口

导入接口→调用接口→处理返回数据

<template>
  <view class="login-container">
    <input v-model="username" placeholder="请输入用户名" />
    <input v-model="password" type="password" placeholder="请输入密码" />
    <button @click="handleLogin">登录</button>
  </view>
</template>

<script setup>
import { ref } from 'vue'
// 方式 1:直接从模块导入(适合接口较少时)
import { userLogin } from '@/api/user'
// 方式 2:从统一导出文件导入(适合接口较多时)
// import { userLogin } from '@/api'

// 定义表单数据
const username = ref('')
const password = ref('')

// 登录按钮点击事件
const handleLogin = async () => {
  // 1. 表单验证(可选,前端先做基础校验,减少无效请求)
  if (!username.value || !password.value) {
    uni.showToast({ title: '请输入用户名和密码', icon: 'none' })
    return
  }

  try {
    // 2. 调用封装的登录接口,传递参数(对象格式,对应后端要求)
    const res = await userLogin({
      username: username.value,
      password: password.value
    })

    // 3. 处理接口返回数据(res 就是后端返回的 res.data,已在 request.js 中处理)
    console.log('登录成功,用户信息:', res)
    // 存储 token 到本地(供后续接口请求使用)
    uni.setStorageSync('userToken', res.token)
    // 跳转首页
    uni.switchTab({ url: '/pages/index/index' })
  } catch (error) {
    // 4. 处理接口调用失败(如业务错误、网络错误,也可省略,因为 request.js 已做统一提示)
    console.error('登录失败:', error)
  }
}
</script>

6.遇到不同的后端接口(适配改动)

1
后端接口场景:基础地址变更
对应的修改位置:utils/request.js 的 baseURL
具体操作 :直接修改开发 / 生产环境的地址即可;若有多环境(测试 / 预发布),可新增环境变量配置。
2
后端接口场景:身份验证字段不是 Authorization
对应的修改位置:utils/request.js 请求拦截器
具体操作 :把 config.headers.Authorization 改为后端要求的字段,如 config.headers.token。
3
后端接口场景:响应成功码不是 200/0
对应的修改位置:utils/request.js 响应拦截器
具体操作 :把 res.code === 200/res.code === 0改为后端约定的成功码,如res.code === 20000
4
后端接口场景:响应格式不是 {code, data, msg}
对应的修改位置:utils/request.js 响应拦截器
具体操作 :调整 res 的解构方式,比如后端返回 {success: true, result: {}, message: ''},则修改为:if (res.success) { return res.result }
5
后端接口场景:需要传递 FormData 上传文件
对应的修改位置:具体接口(如 api/user.js)
具体操作 :创建 FormData 实例,添加文件 / 参数,覆盖请求头为 multipart/form-data。
6
后端接口场景:接口需要跨域(开发环境)
对应的修改位置:uni-app 项目根目录创建 vite.config.js
具体操作 :配置 vite 代理,转发请求,避免跨域错误(下面补充)。

7.补充:开发环境跨域解决方案(vite 代理)

如果开发时出现「Access to XMLHttpRequest at … from origin … has been blocked by CORS policy」跨域错误,可在项目根目录创建 vite.config.js,配置代理转发:

import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'

export default defineConfig({
  plugins: [uni()],
  server: {
    proxy: {
      // 匹配所有以 /api 开头的请求路径
      '/api': {
        target: 'http://localhost:8080', // 后端真实接口地址(去掉 /api)
        changeOrigin: true, // 开启跨域模拟
        rewrite: (path) => path.replace(/^\/api/, '') // 重写路径:去掉 /api 前缀(根据后端接口是否带 /api 调整)
      }
    }
  }
})
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐