Skip to content

接口联调与调试

前端与后端联调时的常见操作:请求封装、跨域处理、Mock 数据、接口调试。


Axios 封装

为什么要封装 Axios?

如果你不封装,每个组件发请求都要写一遍 axios.get()try/catch、错误处理、token 注入...代码会非常重复。

封装的好处:

  • 统一 baseURL:开发环境用 localhost:3000,生产环境用 api.example.com,一处配置全局生效
  • 自动携带 token:登录后每次请求自动带上 token,不用手动写
  • 统一错误处理:401 自动跳登录页、500 统一提示,不用每个请求都写一遍
  • 统一响应格式:后端返回 { code, data, message },封装后直接返回 data,组件里不用再解构

基础封装

js
// src/utils/request.js
import axios from 'axios'                          // 引入 axios 库
import { getToken, removeToken } from './auth'      // 引入 token 管理函数
import router from '@/router'                       // 引入路由实例,用于跳转登录页

// 创建 axios 实例(axios.create 会返回一个独立的 axios 实例,不影响全局配置)
const service = axios.create({
  baseURL: import.meta.env.VITE_API_URL,            // 从环境变量读取 API 基础地址
  timeout: 15000,                                   // 请求超时时间:15 秒
  headers: {
    'Content-Type': 'application/json'              // 默认请求体格式为 JSON
  }
})

// 请求拦截器:在请求发送前自动执行(拦截器是 axios 的中间件机制)
service.interceptors.request.use(
  config => {
    const token = getToken()                        // 从本地存储获取 token
    if (token) {
      config.headers.Authorization = `Bearer ${token}`  // 在请求头中携带 token
    }
    return config                                   // 必须返回 config,否则请求会被阻塞
  },
  error => Promise.reject(error)                    // 请求配置出错时,直接抛出错误
)

// 响应拦截器:在收到响应后自动执行
service.interceptors.response.use(
  response => {
    // ⚠️ 假设后端返回 { code, data, message } 统一格式
    // 如果后端直接返回数据(无 code 包装),可改为:return response.data
    const { code, data, message } = response.data   // 解构后端返回的统一格式

    if (code === 0) return data                     // 业务成功(code=0),直接返回 data

    // ⚠️ 成功码因后端而异,常见值:0、200、'success'、'000000' 等
    // 需要与后端约定,按实际接口调整上面的判断条件

    // 业务错误(code 非 0)
    console.error(message || '请求失败')             // 打印错误信息
    return Promise.reject(new Error(message))       // 抛出错误,让调用方捕获
  },
  error => {
    if (error.response) {                           // 有响应说明服务器返回了状态码
      switch (error.response.status) {
        case 401:                                   // 未登录或 token 过期
          removeToken()                             // 清除本地 token
          router.push('/login')                     // 跳转到登录页
          break
        case 403:                                   // 无权限访问
          console.error('没有操作权限')
          break
        case 500:                                   // 服务器内部错误
          console.error('服务器异常')
          break
      }
    }
    return Promise.reject(error)                    // 继续抛出错误
  }
)

export default service                              // 导出封装好的 axios 实例

接口模块化

js
// src/api/user.js
import request from '@/utils/request'

export function getUserInfo() {
  return request.get('/api/user/info')
}

export function login(data) {
  return request.post('/api/auth/login', data)
}

export function updateUser(id, data) {
  return request.put(`/api/user/${id}`, data)
}
vue
<!-- 组件中使用 -->
<script setup>
import { ref, onMounted } from 'vue'
import { getUserInfo } from '@/api/user'

const user = ref(null)

onMounted(async () => {
  user.value = await getUserInfo()
})
</script>

环境变量切换 API 地址

不同环境(开发/测试/生产)使用不同的 API 地址,通过 .env 文件配置:

bash
# .env.development(开发环境)
VITE_API_URL=http://localhost:3000

# .env.staging(测试环境)
VITE_API_URL=https://staging-api.example.com

# .env.production(生产环境)
VITE_API_URL=https://api.example.com
bash
# 启动时自动读取对应环境的 .env 文件
npm run dev          # 读取 .env.development
npm run build        # 读取 .env.production
npm run build --mode staging  # 读取 .env.staging

⚠️ Vite 要求自定义环境变量必须以 VITE_ 开头,否则不会暴露给客户端代码。


跨域处理

什么是跨域

浏览器同源策略限制:协议 + 域名 + 端口 三者都相同才算同源

http://localhost:5173  →  http://localhost:3000    ❌ 端口不同
https://a.com          →  https://b.com            ❌ 域名不同
http://a.com:80        →  https://a.com:443        ❌ 协议不同

开发环境:代理

js
// vite.config.ts
import { defineConfig } from 'vite'               // 引入 Vite 配置函数

export default defineConfig({
  server: {
    proxy: {
      '/api': {                                    // 匹配所有以 /api 开头的请求路径
        target: 'http://localhost:3000',           // 代理目标地址(后端服务地址)
        changeOrigin: true,                        // 修改请求头中的 Host 为目标地址(解决跨域)
        rewrite: (path) => path.replace(/^\/api/, '')  // 重写路径:去掉 /api 前缀
      }
    }
  }
})

验证代理是否生效:

bash
# 1. 重启开发服务器
yarn dev

# 2. 在浏览器 Console 中发送请求
fetch('/api/user/info').then(r => r.json()).then(console.log)

# 3. 查看 Network 面板,确认请求被代理到 http://localhost:3000/user/info
前端请求:/api/user/info
       ↓ 代理转发
后端收到:/user/info

生产环境:Nginx 反向代理

nginx
location /api/ {
    proxy_pass http://backend:3000/;  # 末尾 / 表示替换路径:/api/user → /user
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}
# 如果后端路径也包含 /api,改为:proxy_pass http://backend:3000;(不带末尾 /)

Mock 数据

什么是 Mock?为什么要用 Mock?

Mock 就是"假数据"。当后端接口还没开发好时,前端可以用假数据先开发,不被后端阻塞。

什么时候用 Mock?

  • 后端接口未就绪:前端先用假数据开发,等接口好了再替换
  • 演示环境:给领导或客户演示时,需要固定的数据
  • 测试边界情况:空数据、超长数据、错误状态等难以从后端获取的场景
  • 离线开发:没有网络时也能正常开发

本地 Mock(开发阶段)

bash
npm install -D vite-plugin-mock mockjs
js
// mock/user.js
export default [
  {
    url: '/api/user/info',                         // Mock 接口的 URL(与真实接口一致)
    method: 'get',                                 // HTTP 请求方法
    response: () => ({                             // 响应函数,返回模拟数据
      code: 0,                                     // 业务状态码(0 表示成功)
      data: { name: '张三', age: 25, role: 'admin' }  // 模拟的用户数据
    })
  }
]
js
// vite.config.ts
import { viteMockServe } from 'vite-plugin-mock'   // 引入 Mock 插件

export default defineConfig({
  plugins: [
    viteMockServe({
      mockPath: 'mock',                            // Mock 文件存放目录
      localEnabled: true                           // 开发环境启用 Mock
    })
  ]
})

验证 Mock 是否生效:

bash
# 1. 安装依赖
npm install -D vite-plugin-mock mockjs

### 在线 Mock 平台

| 平台 | 说明 |
|------|------|
| [Mock.js](http://mockjs.com/) | 生成随机数据的库 |
| [Apifox](https://apifox.com/) | API 文档 + Mock + 调试 |
| [Postman Mock](https://www.postman.com/) | Postman 内置 Mock |

---

## 接口调试工具

### Chrome DevTools Network
  1. 打开 F12 → Network → 选 Fetch/XHR
  2. 点击某个请求,查看:
    • Headers:请求头、请求参数
    • Payload:请求体(POST/PUT)
    • Preview:响应预览(格式化 JSON)
    • Response:响应原始内容
    • Timing:各阶段耗时

### 接口调试快捷方式

```js
// 在 Console 中直接发请求测试
fetch('/api/user/info')
  .then(r => r.json())
  .then(console.log)

// POST 请求
fetch('/api/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'admin', password: '123456' })
}).then(r => r.json()).then(console.log)

cURL 命令行调试

什么时候用 cURL?

cURL 是命令行发请求的工具,适合:

  • 排除前端代码问题:如果 cURL 能正常返回数据,说明问题在前端代码
  • 复现后端 bug:把请求参数复制出来,在终端里反复测试
  • 自动化测试:在 CI/CD 中用 cURL 测试接口是否正常
bash
# GET 请求
curl https://api.example.com/user/info

# POST 请求
curl -X POST https://api.example.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"123456"}'

# 带 token
curl https://api.example.com/user/info \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."

常见问题

请求发不出去

bash
# 检查清单
1. baseURL 是否正确
2. 代理配置是否生效(重启 dev server)
3. 是否被浏览器插件拦截(如广告拦截器)
4. 是否 CORS 错误(看 Console 报错)
js
// axios 默认不携带 cookie
axios.create({
  withCredentials: true   // 跨域请求携带 cookie
})

// token 方案:请求头携带
config.headers.Authorization = `Bearer ${token}`

文件上传

js
// 使用 FormData
const formData = new FormData()
formData.append('file', file)
formData.append('type', 'avatar')

request.post('/api/upload', formData, {
  headers: { 'Content-Type': 'multipart/form-data' },
  onUploadProgress: (e) => {
    const percent = Math.round((e.loaded / e.total) * 100)
    console.log(`上传进度: ${percent}%`)
  }
})

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准