Skip to content

Latest commit

 

History

History
455 lines (388 loc) · 8.83 KB

File metadata and controls

455 lines (388 loc) · 8.83 KB

API接口总览文档

概述

本文档提供了系统所有API接口的总览和统一配置说明。

文件位置: src/api/index.js

接口模块

核心模块

统一配置

基础配置

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

// API基础配置
export const API_CONFIG = {
  baseURL: process.env.VITE_API_BASE_URL || '/api',
  timeout: 10000,
  withCredentials: true
}

// 通用请求头
export const COMMON_HEADERS = {
  'Content-Type': 'application/json',
  'X-Requested-With': 'XMLHttpRequest'
}

请求拦截器

// 请求拦截器
request.interceptors.request.use(
  config => {
    // 添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    // 添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      }
    }
    
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

响应拦截器

// 响应拦截器
request.interceptors.response.use(
  response => {
    const { data } = response
    
    // 统一处理业务错误
    if (!data.status) {
      ElMessage.error(data.msg || '请求失败')
      return Promise.reject(new Error(data.msg || '请求失败'))
    }
    
    return data
  },
  error => {
    // 统一处理HTTP错误
    const { response } = error
    
    if (response) {
      switch (response.status) {
        case 401:
          ElMessage.error('登录已过期,请重新登录')
          // 跳转到登录页
          router.push('/login')
          break
        case 403:
          ElMessage.error('权限不足')
          break
        case 404:
          ElMessage.error('请求的资源不存在')
          break
        case 500:
          ElMessage.error('服务器内部错误')
          break
        default:
          ElMessage.error('网络请求失败')
      }
    } else {
      ElMessage.error('网络连接失败')
    }
    
    return Promise.reject(error)
  }
)

接口规范

请求格式

// GET请求
const getList = (params) => {
  return request({
    url: '/api/list',
    method: 'get',
    params
  })
}

// POST请求
const createItem = (data) => {
  return request({
    url: '/api/create',
    method: 'post',
    data
  })
}

// PUT请求
const updateItem = (id, data) => {
  return request({
    url: `/api/update/${id}`,
    method: 'put',
    data
  })
}

// DELETE请求
const deleteItem = (id) => {
  return request({
    url: `/api/delete/${id}`,
    method: 'delete'
  })
}

响应格式

// 成功响应
{
  status: true,
  msg: '操作成功',
  data: {
    // 具体数据
  },
  code: 200
}

// 失败响应
{
  status: false,
  msg: '错误信息',
  data: null,
  code: 40001
}

// 分页响应
{
  status: true,
  data: {
    list: [],           // 数据列表
    total: 100,         // 总数量
    page: 1,            // 当前页
    pageSize: 20,       // 每页数量
    totalPages: 5       // 总页数
  }
}

错误码规范

HTTP状态码

  • 200: 请求成功
  • 400: 请求参数错误
  • 401: 未授权,需要登录
  • 403: 权限不足
  • 404: 资源不存在
  • 500: 服务器内部错误

业务错误码

  • 10001-19999: 用户相关错误
  • 20001-29999: 权限相关错误
  • 30001-39999: 数据相关错误
  • 40001-49999: 业务逻辑错误
  • 50001-59999: 系统相关错误

具体错误码

export const ERROR_CODES = {
  // 用户相关
  USER_NOT_FOUND: 10001,
  USER_DISABLED: 10002,
  PASSWORD_ERROR: 10003,
  
  // 权限相关
  NO_PERMISSION: 20001,
  TOKEN_EXPIRED: 20002,
  TOKEN_INVALID: 20003,
  
  // 数据相关
  DATA_NOT_FOUND: 30001,
  DATA_DUPLICATE: 30002,
  DATA_INVALID: 30003,
  
  // 业务逻辑
  OPERATION_FAILED: 40001,
  VALIDATION_FAILED: 40002,
  CONSTRAINT_VIOLATION: 40003
}

接口测试

测试工具

推荐使用以下工具进行API测试:

  • Postman: 功能强大的API测试工具
  • Insomnia: 轻量级REST客户端
  • curl: 命令行工具
  • 浏览器开发者工具: 网络面板

测试环境

// 环境配置
const environments = {
  development: {
    baseURL: 'http://localhost:3000/api',
    timeout: 5000
  },
  testing: {
    baseURL: 'http://test-api.example.com/api',
    timeout: 10000
  },
  production: {
    baseURL: 'https://api.example.com/api',
    timeout: 15000
  }
}

测试用例示例

// 登录接口测试
describe('Auth API', () => {
  test('登录成功', async () => {
    const loginData = {
      username: 'admin',
      password: '123456',
      captcha: '1234',
      captcha_key: 'test_key'
    }
    
    const result = await login(loginData)
    
    expect(result.status).toBe(true)
    expect(result.data.token).toBeDefined()
    expect(result.data.employeeInfo).toBeDefined()
  })
  
  test('登录失败 - 密码错误', async () => {
    const loginData = {
      username: 'admin',
      password: 'wrong_password',
      captcha: '1234',
      captcha_key: 'test_key'
    }
    
    await expect(login(loginData)).rejects.toThrow()
  })
})

性能优化

请求优化

// 请求去重
const pendingRequests = new Map()

const removePendingRequest = (config) => {
  const requestKey = `${config.method}:${config.url}`
  if (pendingRequests.has(requestKey)) {
    const cancelToken = pendingRequests.get(requestKey)
    cancelToken.cancel('重复请求')
    pendingRequests.delete(requestKey)
  }
}

// 缓存策略
const cache = new Map()

const getCachedResponse = (key) => {
  const cached = cache.get(key)
  if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
    return cached.data
  }
  return null
}

并发控制

// 限制并发请求数量
class RequestQueue {
  constructor(maxConcurrent = 6) {
    this.maxConcurrent = maxConcurrent
    this.running = 0
    this.queue = []
  }
  
  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        requestFn,
        resolve,
        reject
      })
      this.process()
    })
  }
  
  async process() {
    if (this.running >= this.maxConcurrent || this.queue.length === 0) {
      return
    }
    
    this.running++
    const { requestFn, resolve, reject } = this.queue.shift()
    
    try {
      const result = await requestFn()
      resolve(result)
    } catch (error) {
      reject(error)
    } finally {
      this.running--
      this.process()
    }
  }
}

const requestQueue = new RequestQueue()

安全考虑

CSRF防护

// 添加CSRF Token
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
if (csrfToken) {
  request.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken
}

XSS防护

// 输入数据清理
const sanitizeInput = (input) => {
  if (typeof input === 'string') {
    return input
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#x27;')
  }
  return input
}

敏感数据处理

// 请求日志过滤敏感信息
const filterSensitiveData = (data) => {
  const sensitiveFields = ['password', 'token', 'secret']
  const filtered = { ...data }
  
  sensitiveFields.forEach(field => {
    if (filtered[field]) {
      filtered[field] = '***'
    }
  })
  
  return filtered
}

监控和日志

请求监控

// 请求性能监控
const performanceMonitor = {
  start: (config) => {
    config.startTime = Date.now()
  },
  
  end: (config, response) => {
    const duration = Date.now() - config.startTime
    console.log(`API请求耗时: ${config.url} - ${duration}ms`)
    
    // 发送监控数据
    if (duration > 3000) {
      console.warn(`慢请求警告: ${config.url} - ${duration}ms`)
    }
  }
}

错误日志

// 错误日志收集
const errorLogger = {
  log: (error, context) => {
    const errorInfo = {
      message: error.message,
      stack: error.stack,
      url: context.url,
      method: context.method,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    }
    
    // 发送到日志服务
    console.error('API错误:', errorInfo)
  }
}

相关文档


最后更新时间:2025-09-19