签入版本
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
# 9.4 HTTP 请求封装
|
||||
|
||||
:::tip 概述
|
||||
前端使用 Axios 封装 HTTP 请求,自动处理 Token 携带、响应码判断、错误提示。
|
||||
前端使用 Axios 封装 HTTP 请求,自动处理 Token 携带、响应码判断与错误提示(401 时提示重新登录)。
|
||||
:::
|
||||
|
||||
## 封装结构
|
||||
|
||||
```text
|
||||
src/utils/http/axios/
|
||||
ui/src/utils/http/axios/
|
||||
├── index.ts # Axios 实例配置和拦截器
|
||||
├── Axios.ts # VAxios 类封装
|
||||
├── axiosCancel.ts # 请求取消
|
||||
├── axiosTransform.ts # 数据转换接口
|
||||
├── checkStatus.ts # HTTP 状态码处理
|
||||
├── helper.ts # 辅助函数
|
||||
@@ -21,9 +22,10 @@ src/utils/http/axios/
|
||||
### 请求拦截器(自动携带 Token)
|
||||
|
||||
```typescript
|
||||
// src/utils/http/axios/index.ts
|
||||
// ui/src/utils/http/axios/index.ts
|
||||
|
||||
// 请求前自动添加 Token
|
||||
// 请求前自动添加 Token(storage 键名 'ACCESS-TOKEN')
|
||||
const token = storage.get('ACCESS-TOKEN', '');
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
```
|
||||
|
||||
@@ -34,29 +36,53 @@ config.headers['Authorization'] = `Bearer ${token}`;
|
||||
const { code, data, msg } = res.data;
|
||||
|
||||
if (code === 0) {
|
||||
return data; // 成功,返回 data
|
||||
return data; // 成功,返回 data(拦截器已解包)
|
||||
}
|
||||
|
||||
if (code === 401) {
|
||||
// Token 过期,跳转登录
|
||||
router.push('/login');
|
||||
if (code === 401 || res.status === 401) {
|
||||
// Token 过期 → 弹窗提示 → storage.clear() → 跳转登录(见下方 401 处理)
|
||||
}
|
||||
|
||||
// 其他错误,显示提示
|
||||
if (code === 403 || res.status === 403) {
|
||||
// 权限不足 / 租户异常 / 演示模式拦截
|
||||
ElMessage.error(msg || '无访问权限');
|
||||
}
|
||||
|
||||
// 其他业务错误,显示提示
|
||||
ElMessage.error(msg || '操作失败');
|
||||
```
|
||||
|
||||
:::tip 响应处理规则
|
||||
- `code === 0`:成功,返回 `data`
|
||||
- `code === 401`:Token 过期,自动跳转登录页
|
||||
- `code === 403`:权限不足,显示错误提示
|
||||
- 其它 `code`:业务失败,显示 `msg`
|
||||
- `code/status === 401`:Token 过期,弹窗提示重新登录 → 清除本地存储 → 跳转登录页
|
||||
- `code/status === 403`:权限不足,显示错误提示(`msg` 含权限码或拦截原因)
|
||||
- 其它 `code`:业务失败,显示 `msg`(`1`=业务/FormRequest 错误、`404`=数据不存在、`422`=手动校验、`500`=服务器异常)
|
||||
- HTTP 状态码约定见 [8.1 统一响应格式](/zh/api-spec/response.html)
|
||||
:::
|
||||
|
||||
### 401 处理(实际实现,摘自 `ui/src/utils/http/axios/index.ts`)
|
||||
|
||||
```typescript
|
||||
// code === 401(ResultEnum.TIMEOUT)
|
||||
case ResultEnum.TIMEOUT:
|
||||
if (router.currentRoute.value?.name === PageEnum.BASE_LOGIN_NAME) return;
|
||||
ElMessageBox.confirm('登录身份已失效,请重新登录!', '提示', { type: 'warning' })
|
||||
.then(() => {
|
||||
storage.clear();
|
||||
window.location.href = PageEnum.BASE_LOGIN; // '/login'
|
||||
})
|
||||
.catch(() => {});
|
||||
break;
|
||||
```
|
||||
|
||||
:::info 自动续签能力在后端
|
||||
`ui/` 当前未内置自动续签(401 直接重新登录)。后端 `POST /api/refresh` 刷新能力完整保留,详见 [5.2 认证流程实战](/zh/core/auth-flow.html) 与 [8.6 Token 使用说明](/zh/api-spec/token.html)。
|
||||
:::
|
||||
|
||||
## API 模块编写
|
||||
|
||||
```typescript
|
||||
// src/api/tool/example.ts
|
||||
// ui/src/api/tool/example.ts
|
||||
import { http } from '@/utils/http/axios';
|
||||
|
||||
export function getExamplePage(params?: Record<string, any>) {
|
||||
@@ -74,17 +100,38 @@ export function exampleAdd(data: any) {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 注意:批量删除为 DELETE(请求体 { ids: [] })
|
||||
export function exampleBatchDelete(data: any) {
|
||||
return http.request({
|
||||
url: '/example/batchDelete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
(完整规范见 [9.13 API 模块编写规范](/zh/frontend/api-module.html)。)
|
||||
|
||||
## 在组件中使用
|
||||
|
||||
```typescript
|
||||
import { getExamplePage, exampleAdd } from '@/api/tool/example';
|
||||
|
||||
// 获取列表
|
||||
// 获取列表(res 已是 data 部分)
|
||||
const res = await getExamplePage({ pageNo: 1, pageSize: 10 });
|
||||
tableData.value = res.records;
|
||||
tableData.value = res.records; // { records, total, current, size }
|
||||
total.value = res.total;
|
||||
|
||||
// 新增
|
||||
await exampleAdd({ name: '测试', status: 1 });
|
||||
```
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| 所有请求 401 | Token 未携带 / 已过期 | 检查拦截器与登录态;401 弹窗确认后清存储并跳登录页 |
|
||||
| 请求 404 | URL 与后端路由不一致 | 核对 `routes/api.php`(注意 `batchDelete` 为 DELETE) |
|
||||
| 请求跨域 | 直连后端且未配代理/CORS | 用 Vite `VITE_PROXY` 代理或后端配 `CORS_ALLOW_ORIGIN` |
|
||||
| 响应取不到 data | 拦截器未解包 | 统一约定拦截器返回 `data`;排查是否绕过了 `http.request` |
|
||||
|
||||
Reference in New Issue
Block a user