@clownlee/http 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/services/request.service.d.ts +6 -0
- package/dist/http/types/request.types.d.ts +118 -0
- package/dist/http/utils/request-interceptor.d.ts +9 -0
- package/dist/http/utils/response-interceptor.d.ts +12 -0
- package/dist/http/utils/token-refresh.d.ts +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +368 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @fly/request
|
|
2
|
+
|
|
3
|
+
Axios 公共通用库,提供统一的 HTTP 请求能力和自动 token 刷新功能。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- ✅ TypeScript 支持
|
|
8
|
+
- ✅ baseUrl、headers 等配置封装
|
|
9
|
+
- ✅ 请求拦截器和响应拦截器
|
|
10
|
+
- ✅ 支持所有 HTTP 方法(get、post、put、patch、delete 等)
|
|
11
|
+
- ✅ 手动终止请求功能
|
|
12
|
+
- ✅ 自动添加 Bearer token
|
|
13
|
+
- ✅ 401 时自动刷新 token(通过 refreshToken)
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @fly/request
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使用示例
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { init } from '@fly/request'
|
|
25
|
+
import { useUserStore } from '@/stores/user'
|
|
26
|
+
|
|
27
|
+
const userStore = useUserStore()
|
|
28
|
+
|
|
29
|
+
const http = init({
|
|
30
|
+
store: userStore, // pinia 实例
|
|
31
|
+
baseUrl: 'https://api.example.com',
|
|
32
|
+
timeout: 10000,
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json'
|
|
35
|
+
},
|
|
36
|
+
refresh: {
|
|
37
|
+
baseUrl: 'https://api.example.com', // 一般情况 = 最外层的 baseUrl
|
|
38
|
+
path: '/api/v1/auth/refresh',
|
|
39
|
+
name: 'Authorization', // 请求头名称
|
|
40
|
+
token: userStore.refreshToken // 通过 pinia localStorage 存储的 refreshToken 值
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// 使用 HTTP 方法
|
|
45
|
+
const data = await http.get('/api/users')
|
|
46
|
+
const result = await http.post('/api/users', { name: 'John' })
|
|
47
|
+
await http.put('/api/users/1', { name: 'Jane' })
|
|
48
|
+
await http.patch('/api/users/1', { name: 'Jane' })
|
|
49
|
+
await http.delete('/api/users/1')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### init(config)
|
|
55
|
+
|
|
56
|
+
初始化 HTTP 实例。
|
|
57
|
+
|
|
58
|
+
#### 参数
|
|
59
|
+
|
|
60
|
+
- `config.store`: Pinia store 实例,用于存储和获取 token
|
|
61
|
+
- `config.baseUrl`: 接口域名
|
|
62
|
+
- `config.timeout`: 超时时间(毫秒)
|
|
63
|
+
- `config.headers`: 请求头信息
|
|
64
|
+
- `config.refresh`: Token 刷新配置
|
|
65
|
+
- `refresh.baseUrl`: 刷新接口域名
|
|
66
|
+
- `refresh.path`: 刷新接口路径
|
|
67
|
+
- `refresh.name`: 请求头名称(默认为 'Authorization')
|
|
68
|
+
- `refresh.token`: refreshToken 值
|
|
69
|
+
|
|
70
|
+
#### 返回值
|
|
71
|
+
|
|
72
|
+
返回 HTTP 实例,包含以下方法:
|
|
73
|
+
- `get(url, params?, config?)`
|
|
74
|
+
- `post(url, data?, config?)`
|
|
75
|
+
- `put(url, data?, config?)`
|
|
76
|
+
- `patch(url, data?, config?)`
|
|
77
|
+
- `delete(url, config?)`
|
|
78
|
+
- `request(config)`
|
|
79
|
+
- `createCancelToken()`: 创建取消令牌
|
|
80
|
+
- `cancelRequest(cancelToken, message?)`: 手动终止请求
|
|
81
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { RequestInitConfig, RefreshConfig, HttpRequestConfig, HttpResponse, HttpInstance, TokenResponse } from './types/request.types';
|
|
2
|
+
import { init } from './services/request.service';
|
|
3
|
+
|
|
4
|
+
export type { RequestInitConfig, RefreshConfig, HttpRequestConfig, HttpResponse, HttpInstance, TokenResponse, };
|
|
5
|
+
export default init;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { AxiosRequestConfig, AxiosResponse, CancelToken, CancelTokenSource, InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
import { Store } from 'pinia';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Token 刷新配置
|
|
6
|
+
*/
|
|
7
|
+
export interface RefreshConfig {
|
|
8
|
+
/** 刷新接口域名 */
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
/** 刷新接口路径 */
|
|
11
|
+
path: string;
|
|
12
|
+
/** 请求头名称(默认为 'Authorization') */
|
|
13
|
+
name?: string;
|
|
14
|
+
/** refreshToken 值(通过 pinia localStorage 存储) */
|
|
15
|
+
token: string;
|
|
16
|
+
/** refreshToken 值 添加前缀(默认为 'Bearer') */
|
|
17
|
+
prefix?: 'Bearer' | 'Basic' | 'Digest' | 'Token' | 'OAuth' | 'Custom';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 请求初始化配置
|
|
21
|
+
*/
|
|
22
|
+
export interface RequestInitConfig {
|
|
23
|
+
/** Pinia store 实例,用于存储和获取 token */
|
|
24
|
+
store: Store;
|
|
25
|
+
/** 接口域名 */
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
/** 超时时间(毫秒) */
|
|
28
|
+
timeout?: number;
|
|
29
|
+
/** 请求头信息 */
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
/** Token 刷新配置 */
|
|
32
|
+
refresh: RefreshConfig;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* HTTP 请求配置
|
|
36
|
+
*/
|
|
37
|
+
export interface HttpRequestConfig extends Omit<AxiosRequestConfig, 'cancelToken' | 'headers'> {
|
|
38
|
+
/** 请求 URL */
|
|
39
|
+
url: string;
|
|
40
|
+
/** 请求方法 */
|
|
41
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
42
|
+
/** 查询参数 */
|
|
43
|
+
params?: Record<string, any>;
|
|
44
|
+
/** 请求体数据 */
|
|
45
|
+
data?: any;
|
|
46
|
+
/** 请求头 */
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
/** 超时时间 */
|
|
49
|
+
timeout?: number;
|
|
50
|
+
/** 取消令牌(支持 CancelTokenSource 或 CancelToken) */
|
|
51
|
+
cancelToken?: CancelTokenSource | CancelToken;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* HTTP 响应数据模型
|
|
55
|
+
*/
|
|
56
|
+
export interface HttpResponse<T = any> extends Omit<AxiosResponse<T>, 'config'> {
|
|
57
|
+
data: T;
|
|
58
|
+
status: number;
|
|
59
|
+
statusText: string;
|
|
60
|
+
headers: Record<string, string>;
|
|
61
|
+
config: HttpRequestConfig;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* HTTP 实例接口
|
|
65
|
+
*/
|
|
66
|
+
export interface HttpInstance {
|
|
67
|
+
/**
|
|
68
|
+
* GET 请求
|
|
69
|
+
*/
|
|
70
|
+
get<T = any>(url: string, params?: Record<string, any> | null, config?: AxiosRequestConfig): Promise<HttpResponse<T>>;
|
|
71
|
+
/**
|
|
72
|
+
* POST 请求
|
|
73
|
+
*/
|
|
74
|
+
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<HttpResponse<T>>;
|
|
75
|
+
/**
|
|
76
|
+
* PUT 请求
|
|
77
|
+
*/
|
|
78
|
+
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<HttpResponse<T>>;
|
|
79
|
+
/**
|
|
80
|
+
* PATCH 请求
|
|
81
|
+
*/
|
|
82
|
+
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<HttpResponse<T>>;
|
|
83
|
+
/**
|
|
84
|
+
* DELETE 请求
|
|
85
|
+
*/
|
|
86
|
+
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<HttpResponse<T>>;
|
|
87
|
+
/**
|
|
88
|
+
* 通用请求方法
|
|
89
|
+
*/
|
|
90
|
+
request<T = any>(config: HttpRequestConfig): Promise<HttpResponse<T>>;
|
|
91
|
+
/**
|
|
92
|
+
* 创建取消令牌
|
|
93
|
+
*/
|
|
94
|
+
createCancelToken(): CancelTokenSource;
|
|
95
|
+
/**
|
|
96
|
+
* 手动终止请求
|
|
97
|
+
*/
|
|
98
|
+
cancelRequest(cancelToken: CancelTokenSource, message?: string): void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 内部请求配置(包含刷新配置)
|
|
102
|
+
*/
|
|
103
|
+
export interface InternalRequestConfig extends InternalAxiosRequestConfig {
|
|
104
|
+
_refreshConfig?: RefreshConfig;
|
|
105
|
+
_store?: Store;
|
|
106
|
+
_baseUrl?: string;
|
|
107
|
+
_retry?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Token 响应数据模型
|
|
111
|
+
*/
|
|
112
|
+
export interface TokenResponse {
|
|
113
|
+
accessToken: string;
|
|
114
|
+
refreshToken?: string;
|
|
115
|
+
tokenType?: string;
|
|
116
|
+
expiresIn?: number;
|
|
117
|
+
[key: string]: any;
|
|
118
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
import { Store } from 'pinia';
|
|
3
|
+
import { InternalRequestConfig, RefreshConfig } from '../types/request.types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 处理请求拦截器
|
|
7
|
+
* 自动添加 Bearer token
|
|
8
|
+
*/
|
|
9
|
+
export declare function handleRequestInterceptor(config: InternalRequestConfig, store: Store, refreshConfig: RefreshConfig): InternalAxiosRequestConfig;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AxiosError, AxiosResponse } from 'axios';
|
|
2
|
+
import { RefreshConfig } from '../types/request.types';
|
|
3
|
+
import { Store } from 'pinia';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 处理响应拦截器(成功响应)
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleResponseInterceptor<T = any>(response: AxiosResponse<T>, store: Store, refreshConfig: RefreshConfig): Promise<AxiosResponse<T>>;
|
|
9
|
+
/**
|
|
10
|
+
* 处理响应错误拦截器
|
|
11
|
+
*/
|
|
12
|
+
export declare function handleResponseError(error: AxiosError): Promise<any>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Store } from 'pinia';
|
|
2
|
+
import { RefreshConfig } from '../types/request.types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 处理 Token 刷新
|
|
6
|
+
*/
|
|
7
|
+
export declare function handleTokenRefresh(refreshConfig: RefreshConfig, store: Store, refreshTokenFromResponse: string | undefined, retryFn: () => Promise<any>): Promise<any>;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
function getAccessTokenFromStore(store) {
|
|
6
|
+
const state = store.$state;
|
|
7
|
+
if (state && typeof state.accessToken === "string") {
|
|
8
|
+
return state.accessToken;
|
|
9
|
+
}
|
|
10
|
+
if (store.accessToken && typeof store.accessToken === "string") {
|
|
11
|
+
return store.accessToken;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const stored = localStorage.getItem("accessToken");
|
|
15
|
+
if (stored) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(stored);
|
|
18
|
+
} catch {
|
|
19
|
+
return stored;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function handleRequestInterceptor(config, store, refreshConfig) {
|
|
27
|
+
if (store) {
|
|
28
|
+
const accessToken = getAccessTokenFromStore(store);
|
|
29
|
+
if (accessToken) {
|
|
30
|
+
const headerName = (refreshConfig == null ? void 0 : refreshConfig.name) || "Authorization";
|
|
31
|
+
config.headers = config.headers || {};
|
|
32
|
+
config.headers[headerName] = `Bearer ${accessToken}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return config;
|
|
36
|
+
}
|
|
37
|
+
let isRefreshing = false;
|
|
38
|
+
let refreshPromise = null;
|
|
39
|
+
let failedQueue = [];
|
|
40
|
+
function getRefreshTokenFromStore(store, refreshConfig, name = "refreshToken") {
|
|
41
|
+
const state = store.$state;
|
|
42
|
+
if (state && typeof state[name] === "string") {
|
|
43
|
+
return state[name];
|
|
44
|
+
}
|
|
45
|
+
if (store[name] && typeof store[name] === "string") {
|
|
46
|
+
return store[name];
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const stored = localStorage.getItem(name);
|
|
50
|
+
if (stored) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(stored);
|
|
53
|
+
} catch {
|
|
54
|
+
return stored;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
if (name === "refreshToken" && refreshConfig.token) {
|
|
60
|
+
return refreshConfig.token;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function updateAccessTokenInStore(store, accessToken) {
|
|
65
|
+
const state = store.$state;
|
|
66
|
+
if (state && typeof state.accessToken !== "undefined") {
|
|
67
|
+
state.accessToken = accessToken;
|
|
68
|
+
}
|
|
69
|
+
if (typeof store.setAccessToken === "function") {
|
|
70
|
+
store.setAccessToken(accessToken);
|
|
71
|
+
}
|
|
72
|
+
if (typeof store.updateToken === "function") {
|
|
73
|
+
store.updateToken({ accessToken });
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
localStorage.setItem("accessToken", JSON.stringify(accessToken));
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function refreshTokenApi(refreshConfig, prefix, refreshToken) {
|
|
81
|
+
const { baseUrl, path, name = "Authorization" } = refreshConfig;
|
|
82
|
+
const url = path.startsWith("http") ? path : `${baseUrl}${path}`;
|
|
83
|
+
const response = await axios.post(
|
|
84
|
+
url,
|
|
85
|
+
{},
|
|
86
|
+
{
|
|
87
|
+
headers: {
|
|
88
|
+
[name]: `${prefix} ${refreshToken}`
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
return response.data;
|
|
93
|
+
}
|
|
94
|
+
async function handleTokenRefresh(refreshConfig, store, refreshTokenFromResponse, retryFn) {
|
|
95
|
+
let refreshToken = refreshTokenFromResponse;
|
|
96
|
+
if (!refreshToken) {
|
|
97
|
+
const tokenFromStore = getRefreshTokenFromStore(store, refreshConfig);
|
|
98
|
+
refreshToken = tokenFromStore || void 0;
|
|
99
|
+
}
|
|
100
|
+
let prefix = getRefreshTokenFromStore(store, refreshConfig, "prefix") || "Bearer";
|
|
101
|
+
if (!refreshToken) {
|
|
102
|
+
throw new Error("Refresh token not found");
|
|
103
|
+
}
|
|
104
|
+
if (isRefreshing && refreshPromise) {
|
|
105
|
+
try {
|
|
106
|
+
const newToken = await refreshPromise;
|
|
107
|
+
if (newToken.accessToken) {
|
|
108
|
+
updateAccessTokenInStore(store, newToken.accessToken);
|
|
109
|
+
}
|
|
110
|
+
failedQueue.forEach(({ resolve }) => {
|
|
111
|
+
resolve();
|
|
112
|
+
});
|
|
113
|
+
failedQueue = [];
|
|
114
|
+
return await retryFn();
|
|
115
|
+
} catch (refreshError) {
|
|
116
|
+
failedQueue.forEach(({ reject }) => {
|
|
117
|
+
reject(refreshError);
|
|
118
|
+
});
|
|
119
|
+
failedQueue = [];
|
|
120
|
+
throw refreshError;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
isRefreshing = true;
|
|
124
|
+
refreshPromise = refreshTokenApi(refreshConfig, prefix, refreshToken);
|
|
125
|
+
try {
|
|
126
|
+
const newToken = await refreshPromise;
|
|
127
|
+
if (newToken.accessToken) {
|
|
128
|
+
updateAccessTokenInStore(store, newToken.accessToken);
|
|
129
|
+
}
|
|
130
|
+
if (newToken.refreshToken) {
|
|
131
|
+
const state = store.$state;
|
|
132
|
+
if (state && typeof state.refreshToken !== "undefined") {
|
|
133
|
+
state.refreshToken = newToken.refreshToken;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
localStorage.setItem("refreshToken", JSON.stringify(newToken.refreshToken));
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
isRefreshing = false;
|
|
141
|
+
refreshPromise = null;
|
|
142
|
+
failedQueue.forEach(({ resolve }) => {
|
|
143
|
+
resolve();
|
|
144
|
+
});
|
|
145
|
+
failedQueue = [];
|
|
146
|
+
return await retryFn();
|
|
147
|
+
} catch (refreshError) {
|
|
148
|
+
isRefreshing = false;
|
|
149
|
+
refreshPromise = null;
|
|
150
|
+
failedQueue.forEach(({ reject }) => {
|
|
151
|
+
reject(refreshError);
|
|
152
|
+
});
|
|
153
|
+
failedQueue = [];
|
|
154
|
+
throw refreshError;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function handleResponseInterceptor(response, store, refreshConfig) {
|
|
158
|
+
var _a, _b;
|
|
159
|
+
const responseData = response == null ? void 0 : response.data;
|
|
160
|
+
const code = responseData == null ? void 0 : responseData.code;
|
|
161
|
+
if ([2006, 2007, 2003, 2004, 2e3].includes(code)) {
|
|
162
|
+
if (!response.request.responseURL.endsWith("login") && !response.request.responseURL.endsWith("register") && !response.request.responseURL.endsWith("register/tenants")) {
|
|
163
|
+
window.parent.postMessage({
|
|
164
|
+
type: "error",
|
|
165
|
+
data: JSON.stringify({
|
|
166
|
+
code: responseData == null ? void 0 : responseData.code,
|
|
167
|
+
message: responseData == null ? void 0 : responseData.message,
|
|
168
|
+
action: "toLogin"
|
|
169
|
+
})
|
|
170
|
+
}, "*");
|
|
171
|
+
}
|
|
172
|
+
throw new Error(responseData == null ? void 0 : responseData.message);
|
|
173
|
+
} else if (code === 2001) {
|
|
174
|
+
const config = response.config;
|
|
175
|
+
if (!refreshConfig || !store) {
|
|
176
|
+
return response;
|
|
177
|
+
}
|
|
178
|
+
config._retry = true;
|
|
179
|
+
const newAccessToken = (_a = responseData == null ? void 0 : responseData.data) == null ? void 0 : _a.accessToken;
|
|
180
|
+
const refreshToken = (_b = responseData == null ? void 0 : responseData.data) == null ? void 0 : _b.refreshToken;
|
|
181
|
+
const retryFn = async () => {
|
|
182
|
+
const originalRequest = {
|
|
183
|
+
...config,
|
|
184
|
+
headers: {
|
|
185
|
+
...config.headers
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
if (newAccessToken) {
|
|
189
|
+
const headerName = (refreshConfig == null ? void 0 : refreshConfig.name) || "Authorization";
|
|
190
|
+
originalRequest.headers[headerName] = `Bearer ${newAccessToken}`;
|
|
191
|
+
}
|
|
192
|
+
return axios(originalRequest);
|
|
193
|
+
};
|
|
194
|
+
try {
|
|
195
|
+
return await handleTokenRefresh(refreshConfig, store, refreshToken, retryFn);
|
|
196
|
+
} catch (refreshError) {
|
|
197
|
+
throw refreshError;
|
|
198
|
+
}
|
|
199
|
+
} else if (code > 0) {
|
|
200
|
+
throw new Error(responseData == null ? void 0 : responseData.message);
|
|
201
|
+
}
|
|
202
|
+
return responseData;
|
|
203
|
+
}
|
|
204
|
+
async function handleResponseError(error) {
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
class RequestServiceClass {
|
|
208
|
+
constructor() {
|
|
209
|
+
__publicField(this, "instance", null);
|
|
210
|
+
__publicField(this, "config", null);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* 初始化 HTTP 实例
|
|
214
|
+
*/
|
|
215
|
+
init(config) {
|
|
216
|
+
this.config = config;
|
|
217
|
+
this.instance = axios.create({
|
|
218
|
+
baseURL: config.baseUrl,
|
|
219
|
+
timeout: config.timeout || 1e4,
|
|
220
|
+
headers: config.headers || {}
|
|
221
|
+
});
|
|
222
|
+
this.instance.interceptors.request.use(
|
|
223
|
+
(config2) => {
|
|
224
|
+
const internalConfig = config2;
|
|
225
|
+
return handleRequestInterceptor(internalConfig, this.config.store, this.config.refresh);
|
|
226
|
+
},
|
|
227
|
+
(error) => {
|
|
228
|
+
return Promise.reject(error);
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
this.instance.interceptors.response.use(
|
|
232
|
+
async (response) => {
|
|
233
|
+
try {
|
|
234
|
+
return await handleResponseInterceptor(response, this.config.store, this.config.refresh);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
return handleResponseError(error);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
async (error) => {
|
|
240
|
+
return handleResponseError(error);
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* 重置 HTTP 实例
|
|
246
|
+
*/
|
|
247
|
+
reset(config) {
|
|
248
|
+
this.config = config;
|
|
249
|
+
this.instance = axios.create({
|
|
250
|
+
baseURL: config.baseUrl,
|
|
251
|
+
timeout: config.timeout || 1e4,
|
|
252
|
+
headers: config.headers || {}
|
|
253
|
+
});
|
|
254
|
+
this.instance.interceptors.request.use(
|
|
255
|
+
(config2) => {
|
|
256
|
+
const internalConfig = config2;
|
|
257
|
+
return handleRequestInterceptor(internalConfig, this.config.store, this.config.refresh);
|
|
258
|
+
},
|
|
259
|
+
(error) => {
|
|
260
|
+
return Promise.reject(error);
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
this.instance.interceptors.response.use(
|
|
264
|
+
async (response) => {
|
|
265
|
+
return handleResponseInterceptor(response, this.config.store, this.config.refresh);
|
|
266
|
+
},
|
|
267
|
+
async (error) => {
|
|
268
|
+
return handleResponseError(error);
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* 获取 Axios 实例
|
|
274
|
+
*/
|
|
275
|
+
getInstance() {
|
|
276
|
+
if (!this.instance) {
|
|
277
|
+
throw new Error("HTTP instance not initialized. Please call init() first.");
|
|
278
|
+
}
|
|
279
|
+
return this.instance;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* 通用请求方法
|
|
283
|
+
*/
|
|
284
|
+
async request(config) {
|
|
285
|
+
const instance = this.getInstance();
|
|
286
|
+
const axiosConfig = {
|
|
287
|
+
...config,
|
|
288
|
+
// 如果 cancelToken 是 CancelTokenSource,提取其 token 属性
|
|
289
|
+
cancelToken: config.cancelToken && "token" in config.cancelToken ? config.cancelToken.token : config.cancelToken
|
|
290
|
+
};
|
|
291
|
+
const response = await instance.request(axiosConfig);
|
|
292
|
+
return response;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* GET 请求
|
|
296
|
+
*/
|
|
297
|
+
async get(url, params, config) {
|
|
298
|
+
return this.request({
|
|
299
|
+
...config,
|
|
300
|
+
url,
|
|
301
|
+
method: "GET",
|
|
302
|
+
params: params ?? void 0
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* POST 请求
|
|
307
|
+
*/
|
|
308
|
+
async post(url, data, config) {
|
|
309
|
+
return this.request({
|
|
310
|
+
...config,
|
|
311
|
+
url,
|
|
312
|
+
method: "POST",
|
|
313
|
+
data
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* PUT 请求
|
|
318
|
+
*/
|
|
319
|
+
async put(url, data, config) {
|
|
320
|
+
return this.request({
|
|
321
|
+
...config,
|
|
322
|
+
url,
|
|
323
|
+
method: "PUT",
|
|
324
|
+
data
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* PATCH 请求
|
|
329
|
+
*/
|
|
330
|
+
async patch(url, data, config) {
|
|
331
|
+
return this.request({
|
|
332
|
+
...config,
|
|
333
|
+
url,
|
|
334
|
+
method: "PATCH",
|
|
335
|
+
data
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* DELETE 请求
|
|
340
|
+
*/
|
|
341
|
+
async delete(url, config) {
|
|
342
|
+
return this.request({
|
|
343
|
+
...config,
|
|
344
|
+
url,
|
|
345
|
+
method: "DELETE"
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 创建取消令牌
|
|
350
|
+
*/
|
|
351
|
+
createCancelToken() {
|
|
352
|
+
return axios.CancelToken.source();
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 手动终止请求
|
|
356
|
+
*/
|
|
357
|
+
cancelRequest(cancelToken, message) {
|
|
358
|
+
cancelToken.cancel(message);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function init(config) {
|
|
362
|
+
const service = new RequestServiceClass();
|
|
363
|
+
service.init(config);
|
|
364
|
+
return service;
|
|
365
|
+
}
|
|
366
|
+
export {
|
|
367
|
+
init as default
|
|
368
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@clownlee/http",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "公共 HTTP 请求库,提供统一的 HTTP 请求能力和自动 token 刷新功能",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "vite build",
|
|
20
|
+
"dev": "vite build --watch",
|
|
21
|
+
"type-check": "vue-tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"fly",
|
|
25
|
+
"business",
|
|
26
|
+
"package"
|
|
27
|
+
],
|
|
28
|
+
"author": "",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"axios": "^1.6.0",
|
|
32
|
+
"pinia": "^2.1.7",
|
|
33
|
+
"vue": "^3.5.25"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@element-plus/icons-vue": "^2.3.0",
|
|
37
|
+
"element-plus": "^2.12.0",
|
|
38
|
+
"vue-router": "^4.2.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@element-plus/icons-vue": "^2.3.0",
|
|
42
|
+
"@unocss/preset-uno": "^0.58.0",
|
|
43
|
+
"@unocss/vite": "^0.58.0",
|
|
44
|
+
"@types/jquery": "^3.5.33",
|
|
45
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
46
|
+
"element-plus": "^2.12.0",
|
|
47
|
+
"sass": "^1.96.0",
|
|
48
|
+
"terser": "^5.31.0",
|
|
49
|
+
"typescript": "^5.3.0",
|
|
50
|
+
"unocss": "^0.58.0",
|
|
51
|
+
"unplugin-auto-import": "^0.17.0",
|
|
52
|
+
"unplugin-vue-components": "^0.27.0",
|
|
53
|
+
"vite": "^5.0.0",
|
|
54
|
+
"vite-plugin-css-injected-by-js": "^3.5.0",
|
|
55
|
+
"vite-plugin-dts": "^3.9.0",
|
|
56
|
+
"vue-router": "^4.2.0",
|
|
57
|
+
"vue-tsc": "^2.1.10"
|
|
58
|
+
}
|
|
59
|
+
}
|