@nest-omni/core 4.1.3-33 → 4.1.3-34
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/http-client/examples/flexible-response-example.js +6 -6
- package/http-client/examples/index.d.ts +1 -0
- package/http-client/examples/index.js +1 -0
- package/http-client/examples/request-options.example.d.ts +58 -0
- package/http-client/examples/request-options.example.js +197 -0
- package/http-client/services/http-client.service.d.ts +42 -20
- package/http-client/services/http-client.service.js +120 -61
- package/package.json +1 -1
|
@@ -24,7 +24,6 @@ function getFullResponseExample(httpClient) {
|
|
|
24
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
25
25
|
// 返回完整的AxiosResponse对象
|
|
26
26
|
const fullResponse = yield httpClient.get('https://api.example.com/data', {}, // config
|
|
27
|
-
'my-client', // clientName
|
|
28
27
|
{ returnType: 'full' });
|
|
29
28
|
// 现在可以访问headers、status等信息
|
|
30
29
|
console.log('Status:', fullResponse.status);
|
|
@@ -40,7 +39,7 @@ function getFullResponseExample(httpClient) {
|
|
|
40
39
|
function getCustomResponseExample(httpClient) {
|
|
41
40
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
41
|
// 自定义转换函数 - 只返回需要的字段
|
|
43
|
-
const customResponse = yield httpClient.get('https://api.example.com/users', {},
|
|
42
|
+
const customResponse = yield httpClient.get('https://api.example.com/users', {}, {
|
|
44
43
|
returnType: 'custom',
|
|
45
44
|
transform: (response) => {
|
|
46
45
|
var _a;
|
|
@@ -61,7 +60,7 @@ function getCustomResponseExample(httpClient) {
|
|
|
61
60
|
function getPagedResponseExample(httpClient) {
|
|
62
61
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
62
|
var _a, _b;
|
|
64
|
-
const response = yield httpClient.get('https://api.example.com/items?page=1&size=10', {},
|
|
63
|
+
const response = yield httpClient.get('https://api.example.com/items?page=1&size=10', {}, { returnType: 'full' });
|
|
65
64
|
// 提取分页信息
|
|
66
65
|
const paginationInfo = {
|
|
67
66
|
data: response.data,
|
|
@@ -79,7 +78,7 @@ function getPagedResponseExample(httpClient) {
|
|
|
79
78
|
function getRedirectUrlExample(httpClient) {
|
|
80
79
|
return __awaiter(this, void 0, void 0, function* () {
|
|
81
80
|
const response = yield httpClient.get('https://api.example.com/redirect-me', { maxRedirects: 0 }, // 禁止自动重定向以便捕获Location header
|
|
82
|
-
|
|
81
|
+
{ returnType: 'full' });
|
|
83
82
|
// 对于3xx重定向状态,获取重定向URL
|
|
84
83
|
if (response.status >= 300 && response.status < 400) {
|
|
85
84
|
const redirectUrl = response.headers['location'];
|
|
@@ -92,7 +91,7 @@ function getRedirectUrlExample(httpClient) {
|
|
|
92
91
|
// 示例5: 向后兼容 - 默认行为(只返回data)
|
|
93
92
|
function getDefaultBehaviorExample(httpClient) {
|
|
94
93
|
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
-
// 不传递
|
|
94
|
+
// 不传递options参数,保持原有行为
|
|
96
95
|
const data = yield httpClient.get('https://api.example.com/simple-data');
|
|
97
96
|
// 直接得到data,无需.response.data
|
|
98
97
|
console.log('Simple data:', data);
|
|
@@ -105,7 +104,8 @@ function conditionalReturnExample(httpClient, needHeaders) {
|
|
|
105
104
|
const returnOptions = needHeaders
|
|
106
105
|
? { returnType: 'full' }
|
|
107
106
|
: { returnType: 'data' }; // 默认
|
|
108
|
-
const result = yield httpClient.post('https://api.example.com/process', { action: 'calculate' }, {},
|
|
107
|
+
const result = yield httpClient.post('https://api.example.com/process', { action: 'calculate' }, {}, // config
|
|
108
|
+
returnOptions);
|
|
109
109
|
if (needHeaders) {
|
|
110
110
|
// 处理完整响应
|
|
111
111
|
const fullResponse = result;
|
|
@@ -19,3 +19,4 @@ __exportStar(require("./advanced-usage.example"), exports);
|
|
|
19
19
|
__exportStar(require("./multi-api-configuration.example"), exports);
|
|
20
20
|
__exportStar(require("./axios-config-extended.example"), exports);
|
|
21
21
|
__exportStar(require("./ssl-certificate.example"), exports);
|
|
22
|
+
__exportStar(require("./request-options.example"), exports);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 请求级别配置示例
|
|
3
|
+
* 演示 RequestOptions 的使用方式
|
|
4
|
+
*/
|
|
5
|
+
import { HttpClientService } from '../services/http-client.service';
|
|
6
|
+
export declare class RequestOptionsExample {
|
|
7
|
+
private readonly httpClient;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
constructor(httpClient: HttpClientService);
|
|
10
|
+
/**
|
|
11
|
+
* 示例1:最简单 - 禁用重试
|
|
12
|
+
*/
|
|
13
|
+
disableRetry(): Promise<any>;
|
|
14
|
+
/**
|
|
15
|
+
* 示例2:启用重试
|
|
16
|
+
*/
|
|
17
|
+
enableRetry(): Promise<any>;
|
|
18
|
+
/**
|
|
19
|
+
* 示例3:自定义重试条件
|
|
20
|
+
*/
|
|
21
|
+
customRetryCondition(): Promise<any>;
|
|
22
|
+
/**
|
|
23
|
+
* 示例4:配置超时和返回类型
|
|
24
|
+
*/
|
|
25
|
+
withTimeoutAndReturnType(): Promise<any>;
|
|
26
|
+
/**
|
|
27
|
+
* 示例5:使用客户端名称
|
|
28
|
+
*/
|
|
29
|
+
withClientName(): Promise<any>;
|
|
30
|
+
/**
|
|
31
|
+
* 示例6:自定义重试延迟
|
|
32
|
+
*/
|
|
33
|
+
customRetryDelay(): Promise<any>;
|
|
34
|
+
/**
|
|
35
|
+
* 示例7:组合多个配置
|
|
36
|
+
*/
|
|
37
|
+
combinedOptions(): Promise<any>;
|
|
38
|
+
/**
|
|
39
|
+
* 示例8:在 PRTG 服务中使用
|
|
40
|
+
*/
|
|
41
|
+
prtgCreateDevice(): Promise<any>;
|
|
42
|
+
/**
|
|
43
|
+
* 示例9:简洁写法 - 多个配置
|
|
44
|
+
*/
|
|
45
|
+
multipleSettings(): Promise<any>;
|
|
46
|
+
/**
|
|
47
|
+
* 示例10:添加自定义 headers
|
|
48
|
+
*/
|
|
49
|
+
withHeaders(): Promise<any>;
|
|
50
|
+
/**
|
|
51
|
+
* 示例11:添加查询参数
|
|
52
|
+
*/
|
|
53
|
+
withParams(): Promise<any>;
|
|
54
|
+
/**
|
|
55
|
+
* 示例12:使用自定义转换函数
|
|
56
|
+
*/
|
|
57
|
+
withTransform(): Promise<any>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP 请求级别配置示例
|
|
4
|
+
* 演示 RequestOptions 的使用方式
|
|
5
|
+
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
13
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
14
|
+
};
|
|
15
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
16
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
17
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
18
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
19
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
20
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
21
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
var RequestOptionsExample_1;
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.RequestOptionsExample = void 0;
|
|
27
|
+
const common_1 = require("@nestjs/common");
|
|
28
|
+
const http_client_service_1 = require("../services/http-client.service");
|
|
29
|
+
let RequestOptionsExample = RequestOptionsExample_1 = class RequestOptionsExample {
|
|
30
|
+
constructor(httpClient) {
|
|
31
|
+
this.httpClient = httpClient;
|
|
32
|
+
this.logger = new common_1.Logger(RequestOptionsExample_1.name);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 示例1:最简单 - 禁用重试
|
|
36
|
+
*/
|
|
37
|
+
disableRetry() {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
return yield this.httpClient.get('/api/data', undefined, {
|
|
40
|
+
retry: { enabled: false },
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 示例2:启用重试
|
|
46
|
+
*/
|
|
47
|
+
enableRetry() {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
return yield this.httpClient.post('/api/data', { key: 'value' }, undefined, {
|
|
50
|
+
retry: { enabled: true, retries: 3 },
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 示例3:自定义重试条件
|
|
56
|
+
*/
|
|
57
|
+
customRetryCondition() {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
return yield this.httpClient.get('/api/service', undefined, {
|
|
60
|
+
retry: {
|
|
61
|
+
enabled: true,
|
|
62
|
+
retries: 2,
|
|
63
|
+
retryCondition: (error) => {
|
|
64
|
+
if (!error.response)
|
|
65
|
+
return true;
|
|
66
|
+
return error.response.status === 503;
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 示例4:配置超时和返回类型
|
|
74
|
+
*/
|
|
75
|
+
withTimeoutAndReturnType() {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
const response = yield this.httpClient.request({ url: '/api/data', method: 'GET' }, {
|
|
78
|
+
retry: { enabled: false },
|
|
79
|
+
timeout: 5000,
|
|
80
|
+
returnType: 'full',
|
|
81
|
+
});
|
|
82
|
+
this.logger.log(`Status: ${response.status}`);
|
|
83
|
+
return response.data;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 示例5:使用客户端名称
|
|
88
|
+
*/
|
|
89
|
+
withClientName() {
|
|
90
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
return yield this.httpClient.get('/api/data', undefined, {
|
|
92
|
+
clientName: 'my-api-client',
|
|
93
|
+
retry: { enabled: false },
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 示例6:自定义重试延迟
|
|
99
|
+
*/
|
|
100
|
+
customRetryDelay() {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
return yield this.httpClient.request({ url: '/api/unstable', method: 'GET' }, {
|
|
103
|
+
retry: {
|
|
104
|
+
enabled: true,
|
|
105
|
+
retries: 3,
|
|
106
|
+
retryDelay: (retryCount) => retryCount * 1000,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 示例7:组合多个配置
|
|
113
|
+
*/
|
|
114
|
+
combinedOptions() {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const response = yield this.httpClient.put('/api/resource/123', { status: 'active' }, undefined, {
|
|
117
|
+
clientName: 'api-client',
|
|
118
|
+
retry: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
retries: 2,
|
|
121
|
+
retryCondition: (error) => { var _a; return ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 503; },
|
|
122
|
+
retryDelay: (retryCount) => Math.pow(3, retryCount) * 1000,
|
|
123
|
+
},
|
|
124
|
+
timeout: 10000,
|
|
125
|
+
returnType: 'full',
|
|
126
|
+
});
|
|
127
|
+
return response.data;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 示例8:在 PRTG 服务中使用
|
|
132
|
+
*/
|
|
133
|
+
prtgCreateDevice() {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
return yield this.httpClient.post('experimental/groups/123/device', {
|
|
136
|
+
basic: {
|
|
137
|
+
name: 'MyDevice',
|
|
138
|
+
host: '192.168.1.1',
|
|
139
|
+
tags: ['sdwan'],
|
|
140
|
+
},
|
|
141
|
+
}, undefined, {
|
|
142
|
+
retry: { enabled: false },
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 示例9:简洁写法 - 多个配置
|
|
148
|
+
*/
|
|
149
|
+
multipleSettings() {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
return yield this.httpClient.get('/api/data', undefined, {
|
|
152
|
+
clientName: 'prtg-v2-apac',
|
|
153
|
+
retry: { enabled: false },
|
|
154
|
+
timeout: 10000,
|
|
155
|
+
returnType: 'full',
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 示例10:添加自定义 headers
|
|
161
|
+
*/
|
|
162
|
+
withHeaders() {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
return yield this.httpClient.get('/api/data', { headers: { 'X-Custom-Header': 'value' } }, {
|
|
165
|
+
retry: { enabled: false },
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 示例11:添加查询参数
|
|
171
|
+
*/
|
|
172
|
+
withParams() {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
return yield this.httpClient.get('/api/data', { params: { page: 1, limit: 10 } });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 示例12:使用自定义转换函数
|
|
179
|
+
*/
|
|
180
|
+
withTransform() {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
return yield this.httpClient.get('/api/data', undefined, {
|
|
183
|
+
returnType: 'custom',
|
|
184
|
+
transform: (response) => ({
|
|
185
|
+
id: response.data.id,
|
|
186
|
+
name: response.data.name.toUpperCase(),
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
exports.RequestOptionsExample = RequestOptionsExample;
|
|
194
|
+
exports.RequestOptionsExample = RequestOptionsExample = RequestOptionsExample_1 = __decorate([
|
|
195
|
+
(0, common_1.Injectable)(),
|
|
196
|
+
__metadata("design:paramtypes", [http_client_service_1.HttpClientService])
|
|
197
|
+
], RequestOptionsExample);
|
|
@@ -12,9 +12,22 @@ interface ExtendedAxiosRequestConfig extends AxiosRequestConfig {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* 请求级别配置选项
|
|
16
|
+
* 用于在单次请求中覆盖默认配置
|
|
16
17
|
*/
|
|
17
|
-
interface
|
|
18
|
+
export interface RequestOptions {
|
|
19
|
+
/** 客户端名称 */
|
|
20
|
+
clientName?: string;
|
|
21
|
+
/** 重试配置(请求级别,覆盖装饰器和全局配置) */
|
|
22
|
+
retry?: {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
retries?: number;
|
|
25
|
+
retryCondition?: (error: any) => boolean;
|
|
26
|
+
retryDelay?: (retryCount: number) => number;
|
|
27
|
+
shouldResetTimeout?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** 超时配置(请求级别) */
|
|
30
|
+
timeout?: number;
|
|
18
31
|
/** 返回类型: 'data'(默认) | 'full' | 'custom' */
|
|
19
32
|
returnType?: 'data' | 'full' | 'custom';
|
|
20
33
|
/** 自定义返回转换函数 */
|
|
@@ -65,15 +78,23 @@ export declare class HttpClientService {
|
|
|
65
78
|
/**
|
|
66
79
|
* 执行HTTP请求
|
|
67
80
|
* @param config 请求配置
|
|
68
|
-
* @param
|
|
69
|
-
* @
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
81
|
+
* @param options 请求级别配置选项
|
|
82
|
+
* @returns 响应数据
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // 禁用重试
|
|
86
|
+
* await this.request({ url: '/api/data' }, { retry: { enabled: false } })
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // 完整配置
|
|
90
|
+
* await this.request({ url: '/api/data' }, {
|
|
91
|
+
* clientName: 'api-client',
|
|
92
|
+
* retry: { enabled: true, retries: 2 },
|
|
93
|
+
* timeout: 5000,
|
|
94
|
+
* returnType: 'full'
|
|
95
|
+
* })
|
|
75
96
|
*/
|
|
76
|
-
request<T = any>(config: ExtendedAxiosRequestConfig,
|
|
97
|
+
request<T = any>(config: ExtendedAxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
77
98
|
/**
|
|
78
99
|
* 生成curl命令(动态生成,不存储)
|
|
79
100
|
*/
|
|
@@ -94,26 +115,27 @@ export declare class HttpClientService {
|
|
|
94
115
|
* 获取客户端名称
|
|
95
116
|
*/
|
|
96
117
|
getName(): string | undefined;
|
|
97
|
-
get<T = any>(url: string, config?: AxiosRequestConfig,
|
|
98
|
-
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig,
|
|
99
|
-
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig,
|
|
100
|
-
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig,
|
|
101
|
-
delete<T = any>(url: string, config?: AxiosRequestConfig,
|
|
118
|
+
get<T = any>(url: string, config?: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
119
|
+
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
120
|
+
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
121
|
+
patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
122
|
+
delete<T = any>(url: string, config?: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
102
123
|
/**
|
|
103
124
|
* 带等待锁的认证请求方法
|
|
104
125
|
* 用于需要确保只有一个进程执行认证相关操作的场景
|
|
105
126
|
*
|
|
106
127
|
* @param config 请求配置
|
|
107
128
|
* @param tokenProvider token提供函数
|
|
108
|
-
* @param
|
|
129
|
+
* @param lockOptions 等待锁选项
|
|
130
|
+
* @param requestOptions HTTP请求选项
|
|
109
131
|
* @returns 响应数据
|
|
110
132
|
*/
|
|
111
|
-
authRequest<T = any>(config: AxiosRequestConfig, tokenProvider: () => Promise<string>,
|
|
133
|
+
authRequest<T = any>(config: AxiosRequestConfig, tokenProvider: () => Promise<string>, lockOptions?: {
|
|
112
134
|
lockKey?: string;
|
|
113
135
|
lockTimeout?: number;
|
|
114
136
|
waitTimeout?: number;
|
|
115
137
|
enableRetry?: boolean;
|
|
116
|
-
},
|
|
138
|
+
}, requestOptions?: RequestOptions): Promise<T>;
|
|
117
139
|
/**
|
|
118
140
|
* 带等待锁的批量认证请求
|
|
119
141
|
* 用于需要批量执行认证相关操作但避免重复认证的场景
|
|
@@ -126,12 +148,12 @@ export declare class HttpClientService {
|
|
|
126
148
|
authBatchRequest<T = any>(requests: Array<{
|
|
127
149
|
config: AxiosRequestConfig;
|
|
128
150
|
key: string;
|
|
129
|
-
}>, tokenProvider: () => Promise<string>,
|
|
151
|
+
}>, tokenProvider: () => Promise<string>, lockOptions?: {
|
|
130
152
|
lockKey?: string;
|
|
131
153
|
lockTimeout?: number;
|
|
132
154
|
waitTimeout?: number;
|
|
133
155
|
maxConcurrency?: number;
|
|
134
|
-
},
|
|
156
|
+
}, requestOptions?: RequestOptions): Promise<Array<T | null>>;
|
|
135
157
|
/**
|
|
136
158
|
* Capture calling context information
|
|
137
159
|
* @returns Calling context with service class and method name
|
|
@@ -96,22 +96,44 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
96
96
|
/**
|
|
97
97
|
* 执行HTTP请求
|
|
98
98
|
* @param config 请求配置
|
|
99
|
-
* @param
|
|
100
|
-
* @
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
99
|
+
* @param options 请求级别配置选项
|
|
100
|
+
* @returns 响应数据
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // 禁用重试
|
|
104
|
+
* await this.request({ url: '/api/data' }, { retry: { enabled: false } })
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* // 完整配置
|
|
108
|
+
* await this.request({ url: '/api/data' }, {
|
|
109
|
+
* clientName: 'api-client',
|
|
110
|
+
* retry: { enabled: true, retries: 2 },
|
|
111
|
+
* timeout: 5000,
|
|
112
|
+
* returnType: 'full'
|
|
113
|
+
* })
|
|
106
114
|
*/
|
|
107
|
-
request(config,
|
|
115
|
+
request(config, options) {
|
|
108
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
109
117
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
// 解析参数
|
|
119
|
+
let effectiveClientName;
|
|
120
|
+
let requestRetryConfig;
|
|
121
|
+
let requestReturnType;
|
|
122
|
+
let requestTransform;
|
|
123
|
+
let requestTimeout;
|
|
124
|
+
if (options) {
|
|
125
|
+
effectiveClientName = options.clientName || this.clientName || '';
|
|
126
|
+
requestRetryConfig = options.retry;
|
|
127
|
+
requestReturnType = options.returnType;
|
|
128
|
+
requestTransform = options.transform;
|
|
129
|
+
requestTimeout = options.timeout;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
effectiveClientName = this.clientName || '';
|
|
133
|
+
}
|
|
134
|
+
// Get decorator context from CallStackExtractor
|
|
113
135
|
// This allows decorators on service methods to pass context to HTTP client
|
|
114
|
-
const effectiveDecoratorContext =
|
|
136
|
+
const effectiveDecoratorContext = call_stack_extractor_util_1.CallStackExtractor.getDecoratorContext();
|
|
115
137
|
// ========== 安全验证开始 ==========
|
|
116
138
|
// 构造完整URL进行验证
|
|
117
139
|
const requestURL = config.url || '';
|
|
@@ -186,7 +208,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
186
208
|
// 应用认证配置
|
|
187
209
|
const authConfig = yield this.applyAuthToConfig(config);
|
|
188
210
|
// 应用装饰器配置
|
|
189
|
-
const enhancedConfig = this.applyDecoratorConfig(authConfig,
|
|
211
|
+
const enhancedConfig = this.applyDecoratorConfig(authConfig, effectiveDecoratorContext);
|
|
190
212
|
// 将 retryRecorder 存储到 config metadata 中,供 onRetry 回调使用
|
|
191
213
|
enhancedConfig.metadata = enhancedConfig.metadata || {};
|
|
192
214
|
enhancedConfig.metadata.retryRecorder = retryRecorder;
|
|
@@ -194,9 +216,17 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
194
216
|
const decoratorConfigs = effectiveDecoratorContext
|
|
195
217
|
? decorators_1.HttpDecoratorUtils.getAllDecoratorConfigs(effectiveDecoratorContext.target, effectiveDecoratorContext.propertyKey)
|
|
196
218
|
: {};
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
219
|
+
// 存储重试配置到 metadata,供 retryCondition 使用
|
|
220
|
+
// 优先级:请求级别配置 > 装饰器配置 > 全局配置
|
|
221
|
+
const retryEnabled = (requestRetryConfig === null || requestRetryConfig === void 0 ? void 0 : requestRetryConfig.enabled) !== undefined
|
|
222
|
+
? requestRetryConfig.enabled
|
|
223
|
+
: (_c = decoratorConfigs.retry) === null || _c === void 0 ? void 0 : _c.enabled;
|
|
224
|
+
if (retryEnabled !== undefined) {
|
|
225
|
+
enhancedConfig.metadata.retryEnabled = retryEnabled;
|
|
226
|
+
}
|
|
227
|
+
// 如果请求级别提供了重试配置,存储到 metadata
|
|
228
|
+
if (requestRetryConfig) {
|
|
229
|
+
enhancedConfig.metadata.requestRetryConfig = requestRetryConfig;
|
|
200
230
|
}
|
|
201
231
|
// 日志记录开始
|
|
202
232
|
const decoratorLogging = decoratorConfigs.logging || {};
|
|
@@ -209,8 +239,8 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
209
239
|
enhancedConfig.metadata = enhancedConfig.metadata || {};
|
|
210
240
|
enhancedConfig.metadata.requestId = requestId;
|
|
211
241
|
}
|
|
212
|
-
//
|
|
213
|
-
const timeout = decoratorConfigs.timeout || this.defaultConfig.timeout;
|
|
242
|
+
// 应用超时配置(优先级:请求级别 > 装饰器 > 全局)
|
|
243
|
+
const timeout = requestTimeout || decoratorConfigs.timeout || this.defaultConfig.timeout;
|
|
214
244
|
if (timeout) {
|
|
215
245
|
enhancedConfig.timeout = timeout;
|
|
216
246
|
}
|
|
@@ -232,20 +262,21 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
232
262
|
const databaseLogging = (loggingOptions === null || loggingOptions === void 0 ? void 0 : loggingOptions.databaseLogging) ||
|
|
233
263
|
((_f = (_e = this.defaultConfig.logging) === null || _e === void 0 ? void 0 : _e.databaseLogging) === null || _f === void 0 ? void 0 : _f.enabled);
|
|
234
264
|
this.logger.debug(`Logging request success with circuitBreakerState: ${circuitBreakerState || 'undefined'}`);
|
|
235
|
-
this.loggingService.logRequestSuccess(response, startTime, requestId, loggingOptions, databaseLogging, retryRecorder.getRecords(), circuitBreakerState,
|
|
265
|
+
this.loggingService.logRequestSuccess(response, startTime, requestId, loggingOptions, databaseLogging, retryRecorder.getRecords(), circuitBreakerState, effectiveDecoratorContext, effectiveClientName, callingContext);
|
|
236
266
|
}
|
|
237
267
|
// 更新统计信息
|
|
238
268
|
this.updateRequestStats(true, Date.now() - startTime, ((_g = response.config) === null || _g === void 0 ? void 0 : _g.method) || 'GET', response.status);
|
|
239
269
|
// 根据返回选项处理响应
|
|
240
|
-
const
|
|
241
|
-
|
|
270
|
+
const returnType = requestReturnType || 'data';
|
|
271
|
+
const transform = requestTransform;
|
|
272
|
+
switch (returnType) {
|
|
242
273
|
case 'full':
|
|
243
274
|
// 返回完整响应对象
|
|
244
275
|
return response;
|
|
245
276
|
case 'custom':
|
|
246
277
|
// 使用自定义转换函数
|
|
247
|
-
if (
|
|
248
|
-
return
|
|
278
|
+
if (transform) {
|
|
279
|
+
return transform(response);
|
|
249
280
|
}
|
|
250
281
|
// 如果没有提供转换函数,回退到返回data
|
|
251
282
|
return response.data;
|
|
@@ -328,29 +359,29 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
328
359
|
return this.clientName;
|
|
329
360
|
}
|
|
330
361
|
// 便捷方法
|
|
331
|
-
get(url, config,
|
|
362
|
+
get(url, config, options) {
|
|
332
363
|
return __awaiter(this, void 0, void 0, function* () {
|
|
333
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }),
|
|
364
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'GET', url }), options);
|
|
334
365
|
});
|
|
335
366
|
}
|
|
336
|
-
post(url, data, config,
|
|
367
|
+
post(url, data, config, options) {
|
|
337
368
|
return __awaiter(this, void 0, void 0, function* () {
|
|
338
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }),
|
|
369
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'POST', url, data }), options);
|
|
339
370
|
});
|
|
340
371
|
}
|
|
341
|
-
put(url, data, config,
|
|
372
|
+
put(url, data, config, options) {
|
|
342
373
|
return __awaiter(this, void 0, void 0, function* () {
|
|
343
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }),
|
|
374
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PUT', url, data }), options);
|
|
344
375
|
});
|
|
345
376
|
}
|
|
346
|
-
patch(url, data, config,
|
|
377
|
+
patch(url, data, config, options) {
|
|
347
378
|
return __awaiter(this, void 0, void 0, function* () {
|
|
348
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }),
|
|
379
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'PATCH', url, data }), options);
|
|
349
380
|
});
|
|
350
381
|
}
|
|
351
|
-
delete(url, config,
|
|
382
|
+
delete(url, config, options) {
|
|
352
383
|
return __awaiter(this, void 0, void 0, function* () {
|
|
353
|
-
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }),
|
|
384
|
+
return this.request(Object.assign(Object.assign({}, config), { method: 'DELETE', url }), options);
|
|
354
385
|
});
|
|
355
386
|
}
|
|
356
387
|
/**
|
|
@@ -359,17 +390,17 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
359
390
|
*
|
|
360
391
|
* @param config 请求配置
|
|
361
392
|
* @param tokenProvider token提供函数
|
|
362
|
-
* @param
|
|
393
|
+
* @param lockOptions 等待锁选项
|
|
394
|
+
* @param requestOptions HTTP请求选项
|
|
363
395
|
* @returns 响应数据
|
|
364
396
|
*/
|
|
365
|
-
authRequest(config, tokenProvider,
|
|
397
|
+
authRequest(config, tokenProvider, lockOptions, requestOptions) {
|
|
366
398
|
return __awaiter(this, void 0, void 0, function* () {
|
|
367
|
-
const effectiveClientName = clientName || this.clientName;
|
|
368
399
|
if (!this.redisLockService) {
|
|
369
400
|
this.logger.warn('RedisLockService not available, proceeding without lock');
|
|
370
|
-
return this.request(config,
|
|
401
|
+
return this.request(config, requestOptions);
|
|
371
402
|
}
|
|
372
|
-
const { lockKey = `auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 30000, waitTimeout = 60000, enableRetry = true, } =
|
|
403
|
+
const { lockKey = `auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 30000, waitTimeout = 60000, enableRetry = true, } = lockOptions || {};
|
|
373
404
|
try {
|
|
374
405
|
// 尝试获取锁并执行请求
|
|
375
406
|
const result = yield this.redisLockService.withLock(lockKey, () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -378,7 +409,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
378
409
|
// 添加认证头
|
|
379
410
|
const authConfig = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }) });
|
|
380
411
|
// 执行请求
|
|
381
|
-
return this.request(authConfig,
|
|
412
|
+
return this.request(authConfig, requestOptions);
|
|
382
413
|
}), {
|
|
383
414
|
ttl: lockTimeout,
|
|
384
415
|
retryCount: enableRetry ? 3 : 0,
|
|
@@ -397,7 +428,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
397
428
|
this.logger.warn('Retrying auth request without lock');
|
|
398
429
|
const token = yield tokenProvider();
|
|
399
430
|
const authConfig = Object.assign(Object.assign({}, config), { headers: Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }) });
|
|
400
|
-
return this.request(authConfig,
|
|
431
|
+
return this.request(authConfig, requestOptions);
|
|
401
432
|
}
|
|
402
433
|
throw error;
|
|
403
434
|
}
|
|
@@ -412,9 +443,8 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
412
443
|
* @param options 等待锁选项
|
|
413
444
|
* @returns 响应数组
|
|
414
445
|
*/
|
|
415
|
-
authBatchRequest(requests, tokenProvider,
|
|
446
|
+
authBatchRequest(requests, tokenProvider, lockOptions, requestOptions) {
|
|
416
447
|
return __awaiter(this, void 0, void 0, function* () {
|
|
417
|
-
const effectiveClientName = clientName || this.clientName;
|
|
418
448
|
if (!this.redisLockService) {
|
|
419
449
|
this.logger.warn('RedisLockService not available, proceeding batch auth requests without lock');
|
|
420
450
|
// 无锁批量执行
|
|
@@ -422,7 +452,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
422
452
|
const promises = requests.map((request) => __awaiter(this, void 0, void 0, function* () {
|
|
423
453
|
try {
|
|
424
454
|
const authConfig = Object.assign(Object.assign({}, request.config), { headers: Object.assign(Object.assign({}, request.config.headers), { Authorization: `Bearer ${token}` }) });
|
|
425
|
-
return yield this.request(authConfig,
|
|
455
|
+
return yield this.request(authConfig, requestOptions);
|
|
426
456
|
}
|
|
427
457
|
catch (error) {
|
|
428
458
|
this.logger.error(`Batch auth request failed for key: ${request.key}`, error);
|
|
@@ -431,7 +461,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
431
461
|
}));
|
|
432
462
|
return Promise.all(promises);
|
|
433
463
|
}
|
|
434
|
-
const { lockKey = `batch-auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 60000, waitTimeout = 120000, maxConcurrency = 5, } =
|
|
464
|
+
const { lockKey = `batch-auth-request:${(0, request_id_util_1.generateRequestId)()}`, lockTimeout = 60000, waitTimeout = 120000, maxConcurrency = 5, } = lockOptions || {};
|
|
435
465
|
try {
|
|
436
466
|
// 使用锁获取token并执行批量请求
|
|
437
467
|
const results = yield this.redisLockService.withLock(lockKey, () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -444,7 +474,7 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
444
474
|
const executeRequest = (requestConfig, key, index) => __awaiter(this, void 0, void 0, function* () {
|
|
445
475
|
try {
|
|
446
476
|
const authConfig = Object.assign(Object.assign({}, requestConfig), { headers: Object.assign(Object.assign({}, requestConfig.headers), { Authorization: `Bearer ${token}` }) });
|
|
447
|
-
const result = yield this.request(authConfig,
|
|
477
|
+
const result = yield this.request(authConfig, requestOptions);
|
|
448
478
|
results[index] = result;
|
|
449
479
|
return result;
|
|
450
480
|
}
|
|
@@ -585,38 +615,67 @@ let HttpClientService = HttpClientService_1 = class HttpClientService {
|
|
|
585
615
|
((retryCount) => Math.pow(2, retryCount) * 1000),
|
|
586
616
|
retryCondition: this.defaultConfig.retry.retryCondition ||
|
|
587
617
|
((error) => {
|
|
588
|
-
var _a;
|
|
589
|
-
// 检查装饰器配置是否禁用了重试
|
|
618
|
+
var _a, _b, _c;
|
|
590
619
|
const requestConfig = error.config;
|
|
591
|
-
|
|
620
|
+
// 1. 检查请求级别的重试配置(最高优先级)
|
|
621
|
+
const requestRetryConfig = (_a = requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.metadata) === null || _a === void 0 ? void 0 : _a.requestRetryConfig;
|
|
622
|
+
if (requestRetryConfig) {
|
|
623
|
+
// 如果请求级别明确设置了 enabled
|
|
624
|
+
if (requestRetryConfig.enabled === false) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
// 如果请求级别提供了自定义 retryCondition
|
|
628
|
+
if (requestRetryConfig.retryCondition) {
|
|
629
|
+
return requestRetryConfig.retryCondition(error);
|
|
630
|
+
}
|
|
631
|
+
// enabled: true 但没有自定义条件,使用默认逻辑
|
|
632
|
+
if (requestRetryConfig.enabled === true) {
|
|
633
|
+
if (!error.response)
|
|
634
|
+
return true;
|
|
635
|
+
const status = error.response.status;
|
|
636
|
+
return status >= 500 || status === 429;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// 2. 检查装饰器配置
|
|
640
|
+
if (((_b = requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.metadata) === null || _b === void 0 ? void 0 : _b.retryEnabled) === false) {
|
|
592
641
|
return false; // 装饰器明确禁用了重试
|
|
593
642
|
}
|
|
594
|
-
//
|
|
595
|
-
if (
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
643
|
+
// 3. 检查装饰器是否启用了重试
|
|
644
|
+
if (((_c = requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.metadata) === null || _c === void 0 ? void 0 : _c.retryEnabled) === true) {
|
|
645
|
+
// 默认重试条件:网络错误、超时、5xx错误、429错误
|
|
646
|
+
if (!error.response)
|
|
647
|
+
return true;
|
|
648
|
+
const status = error.response.status;
|
|
649
|
+
return status >= 500 || status === 429;
|
|
650
|
+
}
|
|
651
|
+
// 4. 默认不重试
|
|
652
|
+
return false;
|
|
599
653
|
}),
|
|
600
654
|
shouldResetTimeout: this.defaultConfig.retry.shouldResetTimeout !== false,
|
|
601
655
|
onRetry: (retryCount, error, requestConfig) => {
|
|
602
|
-
var _a, _b, _c, _d;
|
|
656
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
603
657
|
// 从 request config metadata 中获取 retryRecorder
|
|
604
658
|
const retryRecorder = (_a = requestConfig.metadata) === null || _a === void 0 ? void 0 : _a.retryRecorder;
|
|
659
|
+
const requestRetryConfig = (_b = requestConfig.metadata) === null || _b === void 0 ? void 0 : _b.requestRetryConfig;
|
|
605
660
|
if (retryRecorder) {
|
|
606
|
-
this.logger.debug(`Recording retry attempt ${retryCount} for ${(
|
|
607
|
-
|
|
661
|
+
this.logger.debug(`Recording retry attempt ${retryCount} for ${(_c = requestConfig.method) === null || _c === void 0 ? void 0 : _c.toUpperCase()} ${requestConfig.url}`);
|
|
662
|
+
// 使用请求级别的 retryDelay 或默认配置
|
|
663
|
+
const retryDelay = (requestRetryConfig === null || requestRetryConfig === void 0 ? void 0 : requestRetryConfig.retryDelay) || ((_d = this.defaultConfig.retry) === null || _d === void 0 ? void 0 : _d.retryDelay);
|
|
664
|
+
const retryConfig = retryDelay ? { retryDelay } : this.defaultConfig.retry;
|
|
665
|
+
const retryRecord = retry_recorder_util_1.RetryRecorder.recordRetry(retryCount, error, requestConfig, this.calculateRetryDelay(retryCount, retryConfig));
|
|
608
666
|
retryRecorder.addRecord(retryRecord);
|
|
609
667
|
}
|
|
610
668
|
else {
|
|
611
|
-
this.logger.warn(`RetryRecorder not found in request config metadata for ${(
|
|
669
|
+
this.logger.warn(`RetryRecorder not found in request config metadata for ${(_e = requestConfig.method) === null || _e === void 0 ? void 0 : _e.toUpperCase()} ${requestConfig.url}. Retry records will not be saved.`);
|
|
612
670
|
}
|
|
613
|
-
// 调用用户自定义的 onRetry
|
|
614
|
-
|
|
615
|
-
|
|
671
|
+
// 调用用户自定义的 onRetry 回调(请求级别 > 全局)
|
|
672
|
+
const onRetryCallback = (requestRetryConfig === null || requestRetryConfig === void 0 ? void 0 : requestRetryConfig.onRetry) || ((_f = this.defaultConfig.retry) === null || _f === void 0 ? void 0 : _f.onRetry);
|
|
673
|
+
if (onRetryCallback) {
|
|
674
|
+
onRetryCallback(retryCount, error, requestConfig);
|
|
616
675
|
}
|
|
617
676
|
else {
|
|
618
677
|
// 默认日志
|
|
619
|
-
this.logger.warn(`Retrying request (attempt ${retryCount}): ${(
|
|
678
|
+
this.logger.warn(`Retrying request (attempt ${retryCount}): ${(_g = requestConfig.method) === null || _g === void 0 ? void 0 : _g.toUpperCase()} ${requestConfig.url}`);
|
|
620
679
|
}
|
|
621
680
|
},
|
|
622
681
|
});
|
package/package.json
CHANGED