You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
400 lines
7.6 KiB
400 lines
7.6 KiB
|
2 weeks ago
|
# API 集成指南
|
||
|
|
|
||
|
|
## 概述
|
||
|
|
|
||
|
|
项目已完整配置 axios,包括请求拦截器、响应拦截器和 API 服务层。
|
||
|
|
|
||
|
|
## 文件结构
|
||
|
|
|
||
|
|
```
|
||
|
|
src/api/
|
||
|
|
├── index.ts # axios 实例配置(请求/响应拦截器)
|
||
|
|
└── auth.ts # 登录相关 API 接口定义
|
||
|
|
```
|
||
|
|
|
||
|
|
## 核心概念
|
||
|
|
|
||
|
|
### 1. axios 实例配置 (`src/api/index.ts`)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 创建 axios 实例
|
||
|
|
const instance = axios.create({
|
||
|
|
baseURL: 'http://localhost:3000/api', // 从环境变量读取
|
||
|
|
timeout: 10000
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
**请求拦截器**:自动在请求头添加 Authorization token
|
||
|
|
```typescript
|
||
|
|
instance.interceptors.request.use((config) => {
|
||
|
|
const token = localStorage.getItem('auth_token')
|
||
|
|
if (token) {
|
||
|
|
config.headers.Authorization = `Bearer ${token}`
|
||
|
|
}
|
||
|
|
return config
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
**响应拦截器**:统一处理错误和 token 过期
|
||
|
|
```typescript
|
||
|
|
instance.interceptors.response.use(
|
||
|
|
(response) => response.data, // 成功时返回 data
|
||
|
|
(error) => {
|
||
|
|
if (error.response?.status === 401) {
|
||
|
|
// Token 过期,清除登录状态并跳转到登录页
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. API 服务层 (`src/api/auth.ts`)
|
||
|
|
|
||
|
|
定义所有 API 接口和类型:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 定义请求/响应类型
|
||
|
|
export interface LoginRequest {
|
||
|
|
username: string
|
||
|
|
password: string
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface LoginResponse {
|
||
|
|
code: number
|
||
|
|
message: string
|
||
|
|
data: {
|
||
|
|
token: string
|
||
|
|
user: User
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 定义 API 接口
|
||
|
|
export const loginApi = (credentials: LoginRequest): Promise<LoginResponse> => {
|
||
|
|
return http.post('/auth/login', credentials)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 状态管理 (`src/stores/auth.ts`)
|
||
|
|
|
||
|
|
在 Pinia store 中调用 API:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const login = async (username: string, password: string): Promise<boolean> => {
|
||
|
|
try {
|
||
|
|
const response = await loginApi({ username, password })
|
||
|
|
|
||
|
|
if (response.code === 200 && response.data) {
|
||
|
|
token.value = response.data.token
|
||
|
|
user.value = response.data.user
|
||
|
|
|
||
|
|
// 保存到本地存储
|
||
|
|
localStorage.setItem('auth_token', token.value)
|
||
|
|
localStorage.setItem('user', JSON.stringify(user.value))
|
||
|
|
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('登录失败:', error)
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 登录流程详解
|
||
|
|
|
||
|
|
### 前端请求流程
|
||
|
|
|
||
|
|
```
|
||
|
|
1. 用户在登录页输入用户名和密码
|
||
|
|
↓
|
||
|
|
2. 点击"登录"按钮调用 handleLogin()
|
||
|
|
↓
|
||
|
|
3. authStore.login(username, password) 被调用
|
||
|
|
↓
|
||
|
|
4. loginApi({ username, password }) 发送 HTTP 请求
|
||
|
|
↓
|
||
|
|
5. axios 拦截器处理(添加 headers 等)
|
||
|
|
↓
|
||
|
|
6. 发送 POST /auth/login 请求到后端
|
||
|
|
```
|
||
|
|
|
||
|
|
### 后端需要返回的数据格式
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "登录成功",
|
||
|
|
"data": {
|
||
|
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
|
|
"user": {
|
||
|
|
"id": "1",
|
||
|
|
"username": "admin",
|
||
|
|
"email": "admin@example.com",
|
||
|
|
"role": "admin"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**字段说明:**
|
||
|
|
- `code`: HTTP 状态码(200 表示成功)
|
||
|
|
- `message`: 返回消息
|
||
|
|
- `data.token`: JWT token,用于后续请求认证
|
||
|
|
- `data.user`: 用户信息对象
|
||
|
|
- `id`: 用户 ID
|
||
|
|
- `username`: 用户名
|
||
|
|
- `email`: 邮箱
|
||
|
|
- `role`: 用户角色(admin 或 user)
|
||
|
|
|
||
|
|
## 环境配置
|
||
|
|
|
||
|
|
### `.env.development` (开发环境)
|
||
|
|
```
|
||
|
|
VITE_API_URL=http://localhost:3000/api
|
||
|
|
```
|
||
|
|
|
||
|
|
### `.env.production` (生产环境)
|
||
|
|
```
|
||
|
|
VITE_API_URL=https://api.example.com/api
|
||
|
|
```
|
||
|
|
|
||
|
|
### 在代码中获取环境变量
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const baseURL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api'
|
||
|
|
```
|
||
|
|
|
||
|
|
## 使用示例
|
||
|
|
|
||
|
|
### 1. 调用登录 API
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { loginApi } from '@/api/auth'
|
||
|
|
|
||
|
|
const response = await loginApi({
|
||
|
|
username: 'admin',
|
||
|
|
password: '123456'
|
||
|
|
})
|
||
|
|
|
||
|
|
if (response.code === 200) {
|
||
|
|
console.log('登录成功,用户信息:', response.data.user)
|
||
|
|
console.log('Token:', response.data.token)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 在组件中使用 store
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<script setup lang="ts">
|
||
|
|
import { useAuthStore } from '@/stores/auth'
|
||
|
|
|
||
|
|
const authStore = useAuthStore()
|
||
|
|
|
||
|
|
const login = async () => {
|
||
|
|
const success = await authStore.login('admin', '123456')
|
||
|
|
if (success) {
|
||
|
|
// 登录成功,跳转
|
||
|
|
router.push('/admin/dashboard')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 创建新的 API 接口
|
||
|
|
|
||
|
|
在 `src/api/` 中创建新文件,如 `users.ts`:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import http from './index'
|
||
|
|
|
||
|
|
export interface User {
|
||
|
|
id: number
|
||
|
|
username: string
|
||
|
|
email: string
|
||
|
|
role: string
|
||
|
|
}
|
||
|
|
|
||
|
|
// 获取用户列表
|
||
|
|
export const getUsersApi = (page: number, limit: number) => {
|
||
|
|
return http.get('/users', {
|
||
|
|
params: { page, limit }
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// 创建用户
|
||
|
|
export const createUserApi = (data: User) => {
|
||
|
|
return http.post('/users', data)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 更新用户
|
||
|
|
export const updateUserApi = (id: number, data: Partial<User>) => {
|
||
|
|
return http.put(`/users/${id}`, data)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 删除用户
|
||
|
|
export const deleteUserApi = (id: number) => {
|
||
|
|
return http.delete(`/users/${id}`)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 错误处理
|
||
|
|
|
||
|
|
### 全局错误处理
|
||
|
|
|
||
|
|
axios 响应拦截器已处理以下情况:
|
||
|
|
|
||
|
|
| 状态码 | 处理方式 |
|
||
|
|
|-------|--------|
|
||
|
|
| 401 | Token 过期,清除登录状态,跳转到登录页 |
|
||
|
|
| 其他 4xx/5xx | 返回错误信息 |
|
||
|
|
| 网络错误 | 返回"网络错误"提示 |
|
||
|
|
|
||
|
|
### 在组件中处理错误
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
try {
|
||
|
|
const response = await loginApi({ username, password })
|
||
|
|
// 处理成功
|
||
|
|
} catch (error: any) {
|
||
|
|
ElMessage.error(error?.message || '登录失败')
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 请求示例
|
||
|
|
|
||
|
|
### POST 请求 (登录)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 请求
|
||
|
|
POST /api/auth/login
|
||
|
|
Content-Type: application/json
|
||
|
|
Authorization: Bearer <token>
|
||
|
|
|
||
|
|
{
|
||
|
|
"username": "admin",
|
||
|
|
"password": "123456"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 响应
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "登录成功",
|
||
|
|
"data": {
|
||
|
|
"token": "...",
|
||
|
|
"user": { ... }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### GET 请求 (获取列表)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 请求
|
||
|
|
GET /api/users?page=1&limit=10
|
||
|
|
Authorization: Bearer <token>
|
||
|
|
|
||
|
|
// 响应
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "成功",
|
||
|
|
"data": {
|
||
|
|
"items": [...],
|
||
|
|
"total": 100
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### PUT 请求 (更新)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 请求
|
||
|
|
PUT /api/users/1
|
||
|
|
Content-Type: application/json
|
||
|
|
Authorization: Bearer <token>
|
||
|
|
|
||
|
|
{
|
||
|
|
"username": "new_name",
|
||
|
|
"email": "new@email.com"
|
||
|
|
}
|
||
|
|
|
||
|
|
// 响应
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "更新成功",
|
||
|
|
"data": { ... }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 常见问题
|
||
|
|
|
||
|
|
### Q: Token 过期怎么处理?
|
||
|
|
|
||
|
|
A: 后端返回 401 时,拦截器会自动清除 token 并跳转到登录页。
|
||
|
|
|
||
|
|
### Q: 如何添加其他请求头?
|
||
|
|
|
||
|
|
A: 修改 `src/api/index.ts` 中的拦截器:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
instance.interceptors.request.use((config) => {
|
||
|
|
config.headers['Custom-Header'] = 'value'
|
||
|
|
return config
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Q: 如何处理超时?
|
||
|
|
|
||
|
|
A: 已在 axios 配置中设置 `timeout: 10000`(10秒)。修改 `src/api/index.ts` 中的 `timeout` 值。
|
||
|
|
|
||
|
|
### Q: 如何调用 API 时显示加载状态?
|
||
|
|
|
||
|
|
A: 在组件中使用 `loading` ref:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const loading = ref(false)
|
||
|
|
loading.value = true
|
||
|
|
try {
|
||
|
|
const response = await loginApi(...)
|
||
|
|
} finally {
|
||
|
|
loading.value = false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 集成后端步骤
|
||
|
|
|
||
|
|
1. **启动后端服务**
|
||
|
|
```bash
|
||
|
|
# 后端服务应该运行在 http://localhost:3000
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **更新 API 地址** (如果不同)
|
||
|
|
```
|
||
|
|
修改 .env.development 中的 VITE_API_URL
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **确保后端返回正确格式**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"code": 200,
|
||
|
|
"message": "...",
|
||
|
|
"data": { ... }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **测试登录流程**
|
||
|
|
- 访问 http://localhost:5173
|
||
|
|
- 输入用户名和密码
|
||
|
|
- 查看浏览器控制台的网络请求
|
||
|
|
|
||
|
|
5. **检查 Token 存储**
|
||
|
|
- 打开浏览器开发者工具 → Application → LocalStorage
|
||
|
|
- 应该看到 `auth_token` 和 `user` 字段
|
||
|
|
|
||
|
|
## 下一步
|
||
|
|
|
||
|
|
- [ ] 集成后端登录接口
|
||
|
|
- [ ] 添加用户列表 API (`src/api/users.ts`)
|
||
|
|
- [ ] 添加产品 API (`src/api/products.ts`)
|
||
|
|
- [ ] 添加订单 API (`src/api/orders.ts`)
|
||
|
|
- [ ] 实现 token 刷新机制
|
||
|
|
- [ ] 添加错误日志上报
|