@be-link/http
v1.2.2
Published
共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密
Downloads
306
Readme
@be-link/http
共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密。
特性
- 单例模式,全局统一配置
- 服务器时间同步(解决客户端时间不准问题)
- Token AES 加密(防重放攻击)
- 请求/响应拦截器
- TypeScript 类型支持
- 所有参数由应用方传递,灵活配置
- 自定义 Header 名称(Token、UserId)
- 并发请求时自动合并时间同步(防止重复请求)
- 使用
performance.now()防止客户端系统时间篡改
安装
pnpm add @be-link/http axios crypto-js依赖说明
| 依赖 | 是否必须 | 说明 |
| ----------- | -------- | ----------------- |
| axios | ✅ 必须 | HTTP 客户端 |
| crypto-js | ✅ 必须 | Token 加密(AES) |
快速开始
基本用法
import { beLinkHttp } from '@be-link/http';
// 1. 初始化(应用启动时调用一次)
beLinkHttp.init({
baseURL: 'https://api.example.com',
timeout: 30000,
});
// 2. 发起请求
const users = await beLinkHttp.get('/api/users');
const result = await beLinkHttp.post('/api/users', { name: '张三' });完整配置
import { beLinkHttp } from '@be-link/http';
beLinkHttp.init({
// 基础配置
baseURL: 'https://api.example.com',
timeout: 30000,
headers: {
'X-Custom-Header': 'value',
},
// 时间同步配置
timeSync: {
enabled: true,
syncUrl: 'https://api.example.com/api/time',
syncGapTime: 50000, // 同步间隔阈值(毫秒)
storagePrefix: 'myapp', // 存储 key 前缀
},
// 加密配置
encryption: {
enabled: true,
key: 'your-aes-key-16ch', // 16 位 AES 密钥
iv: 'your-aes-iv-16ch', // 16 位 AES IV
},
// Token 获取函数
getToken: () => localStorage.getItem('token'),
// 用户 ID 获取函数
getUserId: () => localStorage.getItem('userId'),
// 自定义 Header 名称(可选)
tokenHeaderName: 'X-BeLink-Token', // 默认值
userIdHeaderName: 'X-BeLink-UserId', // 默认值
// 自定义请求拦截器
onRequest: (config) => {
console.log('请求发出:', config.url);
return config;
},
// 自定义响应拦截器
onResponse: (response) => {
console.log('响应收到:', response);
return response;
},
// 自定义错误处理
onError: (error) => {
if (error.response?.status === 401) {
window.location.href = '/login';
}
throw error;
},
});API 文档
beLinkHttp
单例请求客户端,提供以下方法:
init(options)
初始化请求客户端,必须在使用其他方法之前调用。
beLinkHttp.init({
baseURL: 'https://api.example.com',
// ... 其他配置
});HTTP 方法
// GET 请求
beLinkHttp.get<T>(url: string, config?: AxiosRequestConfig): Promise<T>
// POST 请求
beLinkHttp.post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
// PUT 请求
beLinkHttp.put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
// PATCH 请求
beLinkHttp.patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
// DELETE 请求
beLinkHttp.delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>
// 通用请求
beLinkHttp.request<T>(config: AxiosRequestConfig): Promise<T>其他方法
// 获取时间同步服务实例
beLinkHttp.getTimeSyncService(): TimeSyncService | null
// 获取加密服务实例
beLinkHttp.getEncryptionService(): EncryptionService | null
// 获取 Axios 实例
beLinkHttp.getAxiosInstance(): AxiosInstance | null
// 重置客户端(清除配置,需重新 init)
beLinkHttp.reset(): void配置选项 RequestOptions
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| ---------------- | ----------------------------------- | :--: | --------------- | ------------------ |
| baseURL | string | ✅ | - | API 基础地址 |
| timeout | number | ❌ | 30000 | 请求超时时间(ms) |
| headers | Record<string, string> | ❌ | {} | 默认请求头 |
| timeSync | TimeSyncConfig | ❌ | - | 时间同步配置 |
| encryption | EncryptionConfig | ❌ | - | 加密配置 |
| getToken | () => string \| null \| undefined | ❌ | - | Token 获取函数 |
| getUserId | () => string \| null \| undefined | ❌ | - | 用户 ID 获取函数 |
| tokenHeaderName | string | ❌ | X-BeLink-Token | Token Header 名称 |
| userIdHeaderName | string | ❌ | X-BeLink-UserId | UserId Header 名称 |
| onRequest | (config) => config | ❌ | - | 自定义请求拦截器 |
| onResponse | (response) => response | ❌ | - | 自定义响应拦截器 |
| onError | (error) => any | ❌ | - | 自定义错误处理 |
TimeSyncConfig
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| ------------- | --------- | :--: | ------ | --------------------- |
| enabled | boolean | ❌ | true | 是否启用时间同步 |
| syncUrl | string | ✅ | - | 时间同步接口 URL |
| syncGapTime | number | ❌ | 50000 | 同步间隔阈值(毫秒) |
| storagePrefix | string | ❌ | - | localStorage key 前缀 |
EncryptionConfig
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| ------- | --------- | :--: | ------ | -------------- |
| enabled | boolean | ❌ | true | 是否启用加密 |
| key | string | ✅ | - | 16 位 AES 密钥 |
| iv | string | ✅ | - | 16 位 AES IV |
使用示例
基础请求
import { beLinkHttp } from '@be-link/http';
// 初始化
beLinkHttp.init({
baseURL: 'https://api.example.com',
});
// GET 请求
const users = await beLinkHttp.get('/api/users');
// 带参数的 GET 请求
const user = await beLinkHttp.get('/api/users/1', {
params: { include: 'profile' },
});
// POST 请求
const newUser = await beLinkHttp.post('/api/users', {
name: '张三',
age: 25,
});
// PUT 请求
const updated = await beLinkHttp.put('/api/users/1', {
name: '李四',
});
// DELETE 请求
await beLinkHttp.delete('/api/users/1');带类型的请求
interface User {
id: number;
name: string;
age: number;
}
interface ApiResult<T> {
success: boolean;
data: T;
message?: string;
}
// 指定响应类型
const result = await beLinkHttp.get<ApiResult<User[]>>('/api/users');
console.log(result.data); // User[]
const user = await beLinkHttp.post<ApiResult<User>>('/api/users', {
name: '张三',
age: 25,
});
console.log(user.data); // User错误处理
beLinkHttp.init({
baseURL: 'https://api.example.com',
onError: (error) => {
// 处理 401 未授权
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
return;
}
// 处理业务错误
if (error.data?.message) {
alert(error.data.message);
}
throw error;
},
});多实例场景
如果需要创建多个独立的请求实例,可以使用 BeLinkHttp 类:
import { BeLinkHttp } from '@be-link/http';
// 创建实例 A
const serviceA = new BeLinkHttp();
serviceA.init({
baseURL: 'https://api-a.example.com',
});
// 创建实例 B
const serviceB = new BeLinkHttp();
serviceB.init({
baseURL: 'https://api-b.example.com',
});
// 分别使用
const dataA = await serviceA.get('/api/data');
const dataB = await serviceB.get('/api/data');工作流程
Token 加密流程
请求发起时:
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ getToken() │────>│ EncryptionService│────>│ 请求头 │
│ 获取 Token │ │ AES(token|+|time)│ │ Authorization │
└─────────────┘ └──────────────────┘ └─────────────────┘
│
▼
TimeSyncService
(服务器时间校正)时间同步流程
1. 请求发起前检查本地时间是否需要同步
2. 如果超过同步间隔阈值(默认 50 秒),发起同步请求
3. 将服务器时间和客户端时间存储到 localStorage
4. 后续加密时使用校正后的时间戳并发控制
当多个请求同时发起时,时间同步使用 Promise 锁机制,确保只发起一次同步请求:
请求1 ──┬── ensureSync() ──> 发起同步 ──> 等待完成 ──> 继续请求
请求2 ──┤ ↓
请求3 ──┼── ensureSync() ──> 检测到锁 ──> 等待同一个 Promise ──> 继续请求
请求4 ──┤
请求5 ──┘防止系统时间篡改
使用 performance.now() 替代 Date.now() 计算时间流逝:
performance.now()返回页面加载后的高精度时间,不受系统时间修改影响- 即使用户修改系统时间,Token 加密的时间戳仍然准确
类型导出
// 单例实例
import { beLinkHttp } from '@be-link/http';
// 类(用于多实例)
import { BeLinkHttp } from '@be-link/http';
// 服务类
import { TimeSyncService, EncryptionService } from '@be-link/http';
// 配置类型
import type { RequestOptions, TimeSyncConfig, EncryptionConfig, ApiResponse } from '@be-link/http';完整示例
import { beLinkHttp } from '@be-link/http';
// 应用入口初始化
beLinkHttp.init({
baseURL: import.meta.env.VITE_API_URL,
timeout: 30000,
// 时间同步
timeSync: {
syncUrl: `${import.meta.env.VITE_API_URL}/api/time`,
},
// Token 加密
encryption: {
key: import.meta.env.VITE_ENCRYPTION_KEY,
iv: import.meta.env.VITE_ENCRYPTION_IV,
},
// Token 获取
getToken: () => localStorage.getItem('token'),
// 用户 ID
getUserId: () => {
const user = JSON.parse(localStorage.getItem('user') || '{}');
return user.id;
},
// 错误处理
onError: (error) => {
if (error.response?.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
}
throw error;
},
});
// API 调用
async function getUserInfo(userId: string) {
return beLinkHttp.get(`/api/users/${userId}`);
}
async function createUser(data: { name: string; age: number }) {
return beLinkHttp.post('/api/users', data);
}
async function updateUser(userId: string, data: { name?: string; age?: number }) {
return beLinkHttp.put(`/api/users/${userId}`, data);
}
async function deleteUser(userId: string) {
return beLinkHttp.delete(`/api/users/${userId}`);
}License
ISC
