@snail-js/api 0.1.1 → 0.1.3

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 CHANGED
@@ -2,145 +2,344 @@
2
2
  <img src="https://img.shields.io/badge/TypeScript-1e80ff"></img>
3
3
  <img src="https://img.shields.io/npm/v/axios?label=axios&labelColor=1e80ff&color=67C23A"></img>
4
4
  </p>
5
- ## 项目介绍
6
5
 
6
+ ## 项目介绍
7
7
  - 基于 Axios 二次封装
8
- - 提供请求基本实例`Snail`,请求实例`Api`
8
+ - 使用`reflect-metadata`创建和处理元数据
9
+ - 提供装饰器方式定义请求,基本实例`Snail`,请求实例`Api`
9
10
 
10
11
  ## 安装
11
-
12
12
  `npm install @snail-js/api`
13
13
 
14
14
  ## 使用
15
15
 
16
- 1. 创建`Snail`实例
16
+ 1. 请开启`TypeScript`相关装饰器配置
17
+
18
+ ```json
19
+ // tsconfig.json
20
+ {
21
+ "module": "ESNext",
22
+ // 模块解析策略
23
+ "moduleResolution": "node",
24
+ "baseUrl": ".",
25
+ // target 必须大于ES6
26
+ "target": "ESNext",
27
+ // lib 需要包含大于ES6的ES版本
28
+ "lib": ["ESNext", "DOM"],
29
+ // 包含reflect-metadata类型
30
+ "types": ["reflect-metadata"],
31
+ "emitDecoratorMetadata": true,
32
+ "experimentalDecorators": true,
33
+
34
+ "skipLibCheck": true,
35
+ "strictNullChecks": false
36
+ }
37
+ ```
38
+
39
+ 2. 创建`Snail`后端基本配置实例
17
40
 
18
41
  ```typescript
19
- import { Snail, SnailConfig, VersioningType, CacheType } from "@snail-js/api";
20
- import { AxiosRequestConfig } from "axios";
21
-
22
- const options:SnailConfig = {
23
- baseUrl:'api',
24
- Versioning: {
25
- type: VersioningType.Uri,
26
- prefix: "v",
27
- defaultVersion: "0.1.0",
28
- },
42
+ // service.ts
43
+ import { Snail, Server } from "@snail-js/api";
44
+
45
+ @Server({
46
+ baseURL: "/api",
29
47
  timeout: 5000,
30
- requestInterceptors: {
31
- onFulfilled(config: AxiosRequestConfig) {
32
- console.log("requestInterceptors:", config.url);
33
- return config
34
- },
35
- },
36
- responseInterceptors: {
37
- onFulfilled(response) {
38
- console.log(response);
39
- return response
40
- },
41
- onRejected(error) {
42
- console.log(error);
43
- },
44
- },
45
- CacheManage: {
46
- type: CacheType.LocalStorage,
47
- ttl: 60, //缓存过期时间
48
- },
49
- }
50
- export SnailInstance = new Snail(options)
48
+ })
49
+ class BackEnd extends Snail {}
50
+
51
+ export const Service = new BackEnd();
51
52
  ```
52
53
 
53
- 2. 创建请求方法实例
54
+ 3. 创建请求实例
54
55
 
55
56
  ```typescript
56
- import { ApiConfig, RequestPipe } from "@snail-js/api";
57
-
58
- import { SnailInstance } from "./snail";
59
- const pipe: RequestPipe = (input) => {
60
- const { data, headers } = input;
61
- const newHeaders = {
62
- ...headers,
63
- pipe: "RequestPipe",
64
- };
65
- return {
66
- data,
67
- headers: newHeaders,
68
- };
69
- };
57
+ // user.ts
58
+ import { Api, Get, Post, Params, Data } from "@snail-js/api";
70
59
 
71
- const transform = (data: any) => {
72
- return {
73
- ...data,
74
- transform: "transform",
75
- };
76
- };
77
-
78
- const options: ApiConfig = {
79
- transform,
80
- version: "0.3.0", //会覆盖defaultVersion
81
- };
82
- const Api = Snail.Get("test", options);
83
- Api.use(pipe);
84
- export const testApi = Api;
85
-
86
- // 配置了hitSource,Api2请求成功时会使Api的缓存失效
87
- // 当你更新了数据时,get请求的缓存失效,会发起请求,这很有用
88
- const Api2 = Snail.Post("test",{hitSource:Api})
89
- export const testApi2 = Api2
60
+ import { Service } from "./service";
61
+
62
+ @Api("user")
63
+ class UserApi {
64
+
65
+ @Get()
66
+ get(@Params("id") id: string) {}
67
+
68
+ @Post()
69
+ create(@Data() user: User) {}
70
+ }
71
+ // 创建并导出api
72
+ export const userApi = Service.createApi(UserApi);
90
73
  ```
91
74
 
92
- 3. 使用请求
75
+ 3. 发送请求
93
76
 
94
77
  ```typescript
95
- import { testApi } from "./testApi";
96
- // 发送时也可临时更改version,便于测试
97
- const res = await TestApi.send({ version: "0.1.0" });
98
- const { Catch, error, data } = res;
99
- if (error == null) {
100
- console.log("Snail-Api:", data);
101
- }
102
- Catch((error) => {
103
- console.log(error);
104
- });
105
- // success {error:null,data}
106
- // error {error,data:null} 附带的相关错误数据会保存在error.cause中
107
- // 某些服务端标记的code !=0的请求,会被捕获为错误,而且携带数据
78
+ import { userApi } from "./user";
79
+
80
+ const res = await userApi.get("1");
81
+ const { error, data } = res;
82
+ if (error !== null) {
83
+ console.log(data);
84
+ }
108
85
  ```
109
86
 
110
- ### Snail 配置
87
+ ### Server 配置
111
88
 
112
- - `baseUrl`:同`Axios`
89
+ - `baseUrl`:同`Axios`,使用`vite.proxy`时,请使用`\`开头,直接跨域请求请填写完整地址
113
90
  - `Versioning`:版本管理器
114
- - type:管理器类型,enum:Uri,Head,Query,Custom
115
- - prifix:前缀
116
- - defaultVersion:全局默认版本
91
+ - type:管理器类型,enum:Uri,Head,Query,Custom
92
+ - prifix:前缀,字符串;添加在版本号前面的字符,默认为`v`
93
+ - defaultVersion:全局默认版本
117
94
  - timenout:全局超时时间,会被 Api 的 timeout 值覆盖
118
- - requestInterceptors:全局请求拦截器
119
- - responseInterceptors:全局响应拦截器
120
95
  - CacheManage:缓存管理器
121
- - type:缓存管理器类型,CacheType,enum:localStorage,IndexDB,Memory
122
- - ttl: 缓存过期时间
96
+ - type:缓存管理器类型,CacheType,`enum:localStorage,IndexDB,Memory`
97
+ - ttl: 缓存过期时间
98
+ - enableLog: 是否打印日志
123
99
 
124
100
  ## Api 配置
125
101
 
126
- - name?: 请求名称,用于hitSource来使缓存失效
127
- - timeout?: 请求超时时间;会覆盖`SnailConfig.timeout`
128
- - version?: 请求版本,会覆盖`SnailConfig.Versioning.defaultVersion`;
129
- - transform?: `(data: any) => T`;响应数据转换器,用于对返回数据进行转换操作
130
- - headers?: Record<string, string>;
131
- - params?: Record<string, string>;
132
- - hitSource?: string | Api;失效源
102
+ - url?: api 请求端点,与 Server 中的`baseUrl`拼接请求地址,,不要使用`/`开头
103
+ - timeout?: 请求超时时间;会覆盖`Server.timeout`
104
+ - version?: 请求版本,会覆盖`Server.Versioning.defaultVersion`;
105
+
106
+ ## 请求方法装饰器
107
+
108
+ - 提供 axios 的全部请求方法`Get,Post,Head,Put,Delete,Patch,Options`
109
+ - path?: string; 请求端点路径,与`baseUrl,api.url`共同拼接组成最终请求路径,不要使用`/`开头
110
+
111
+ ## 参数装饰器
112
+
113
+ ### 查询参数 `@Params`
114
+
115
+ - `@Params(key?:string)`
116
+
117
+ - 单个参数使用
118
+
119
+ ```typescript
120
+ @Api("user")
121
+ class UserApi {
122
+
123
+ @Get()
124
+ get(@Params("id") id: string, @Params("sign") sign: string) {}
125
+ }
126
+ ```
127
+
128
+ > 传入 key,标记单个查询参数,拼接到请求`?k1=v1&k2=v2`
129
+
130
+ - 对象参数使用
131
+
132
+ ```typescript
133
+ class QueryParams {
134
+ id: string;
135
+ sign: string;
136
+ }
137
+
138
+ @Api("user")
139
+ class UserApi {
140
+
141
+ @Get()
142
+ get(@Params() params: QueryParams) {}
143
+ }
144
+ ```
145
+
146
+ > 不传入 key,会被标记为对象类型查询参数;也能自动拼接到请求
147
+
148
+ - 混合使用
149
+
150
+ ```typescript
151
+ class QueryParams {
152
+ id: string;
153
+ sign: string;
154
+ }
155
+
156
+ @Api("user")
157
+ class UserApi {
158
+
159
+ @Get()
160
+ get(@Params() params: QueryParams, @Params("a") a: number) {}
161
+ }
162
+ ```
163
+
164
+ ### 请求数据
165
+
166
+ - `@Data(key?:string)`
167
+ - 使用方式和`@Params`相同,也支持混合使用
168
+
169
+ ## 策略装饰器`@UseStrategy`
170
+
171
+ - `@UseStrategy(Strategy[])`
172
+
173
+ ### 请求策略
174
+
175
+ - 在请求发送前执行,后面的策略返回结果会覆盖前面的策略
176
+ - 必须将处理后的 request 返回
177
+
178
+ ```typescript
179
+ class CustomStrategy extends Strategy {
180
+ applyRequest(request: AxiosRequestConfig) {
181
+ request.headers["Access-Token"] = "abcde";
182
+ return request;
183
+ }
184
+ }
185
+
186
+ // 用在Snail,全局的请求策略
187
+
188
+ @Server({
189
+ baseURL: "/api",
190
+ timeout: 5000,
191
+ })
192
+ @UseStrategy(new CustomStrategy())
193
+ class BackEnd extends Snail<ShanheResponse> {}
194
+ export const Service = new BackEnd();
195
+
196
+ // 用在Api, 当Api下的方法请求时生效
197
+ @Api("test")
198
+ @UseStrategy(new CustomStrategy())
199
+ class Test {}
200
+
201
+ // 用在方法,此方法请求时生效
202
+ @Api("test")
203
+ @UseStrategy(new CustomStrategy())
204
+ class Test {
205
+ @Get()
206
+ @UseStrategy(new CustomStrategy())
207
+ get() {}
208
+ }
209
+ ```
210
+
211
+ ### 响应策略
212
+
213
+ - 在收到服务器响应后执行
214
+ - 必须将处理后的 response 返回
215
+
216
+ ```typescript
217
+ // 如何定义
218
+ class CustomStrategy extends Strategy {
219
+ applyResponse(response: AxiosResponse) {
220
+ const { status } = response;
221
+ if (status == 200) {
222
+ // do something
223
+ }
224
+ return response;
225
+ }
226
+ }
227
+ ```
228
+
229
+ ## 版本管理装饰器`@Versioning`和`@Version`
230
+
231
+ ### 版本管理器`@Versioning(VersioningOption)`
133
232
 
134
- ## send参数配置
233
+ - 全局管理版本
135
234
 
136
- - params?: Record<string, string>; 请求Query参数
137
- - data?: RequestBody; 请求体数据
138
- - version?: string; 请求版本,如设置,会临时使用此版本运行版本管理器
235
+ ```typescript
236
+ @Server({
237
+ baseURL: "/api",
238
+ timeout: 5000,
239
+ })
240
+ @Versioning({
241
+ type: VersioningType.Header,
242
+ defaultVersion: "0.1.0",
243
+ })
244
+ class BackEnd extends Snail<ShanheResponse> {}
245
+
246
+ export const Service = new BackEnd();
247
+ ```
248
+
249
+ #### `VersioningOption`类型
250
+
251
+ ```typescript
252
+ export enum VersioningType {
253
+ Uri,
254
+ Header,
255
+ Query,
256
+ Custom,
257
+ }
258
+
259
+ interface VersioningCommonOption {
260
+ defaultVersion: string;
261
+ }
262
+
263
+ export interface VersioningUriOption extends VersioningCommonOption {
264
+ type: VersioningType.Uri;
265
+ prefix?: string;
266
+ }
267
+
268
+ export interface VersioningHeaderOption extends VersioningCommonOption {
269
+ type: VersioningType.Header;
270
+ header?: string;
271
+ }
272
+
273
+ export interface VersioningQueryOption extends VersioningCommonOption {
274
+ type: VersioningType.Query;
275
+ key?: string;
276
+ }
277
+
278
+ export interface VersioningCustomOption extends VersioningCommonOption {
279
+ type: VersioningType.Custom;
280
+ extractor: (requestOptions: unknown) => {
281
+ url: string;
282
+ headers: Record<string, any>;
283
+ };
284
+ }
285
+
286
+ export type VersioningOption =
287
+ | VersioningUriOption
288
+ | VersioningHeaderOption
289
+ | VersioningQueryOption
290
+ | VersioningCustomOption;
291
+ ```
292
+
293
+ ### 临时版本修改器`@Version`
294
+ - 临时改变方法请求的版本
295
+ ```typescript
296
+ @Api("test")
297
+ class Test {
298
+
299
+ @Get("HelloWorld")
300
+ @Version("0.2.0")
301
+ test() {}
302
+ }
303
+ ```
304
+
305
+ > 临时改变 api 版本,便于测试
306
+
307
+ ### 缓存装饰器`@Cache`
308
+ - `@Cache(string | null)`
309
+ - 当设置为null时,此方法不应用缓存
310
+ - 当设置为string时,应为此Api类下的方法名称,当设置的此名称方法被调用且正常响应时,被装饰的方法缓存失效
311
+
312
+ ```typescript
313
+
314
+ @Api("test")
315
+ class Test {
316
+
317
+ @Get("HelloWorld")
318
+ @Cache("test2")
319
+ test1() {}
320
+
321
+ @Post()
322
+ test2() {}
323
+
324
+ @Get()
325
+ @Cache(null)
326
+ test3() {}
327
+ }
328
+ ```
329
+
330
+ > 当请求`[Post]test`成功时,`[Get]test/HelloWorld`的缓存失效
331
+ > 注意:`test2`方法请求成功的前提是需要设置`@Cache(null)`,否则仅第一次请求会发送,后续请求需等待缓存管理设置的ttl时间到期才会发送请求
332
+ > 因此,若未设置`test2`方法的`@Cache(null)`,仅第一次请求会使`[Get]test/HelloWorld`的缓存失效,后续需等待ttl时间到期,才会继续失效
333
+
334
+ > `test3`方法请求成功时,不缓存
335
+
336
+ > 注意:要使用缓存,请配置`@Server({CacheManage})`缓存管理器
139
337
 
140
338
  ### 代码仓库
339
+
141
340
  - ![Static Badge](https://img.shields.io/badge/snail-js?style=flat&label=gitee&labelColor=F56C6C&link=https%3A%2F%2Fgitee.com%2Flimich%2Fsnail)
142
341
  - ![Static Badge](https://img.shields.io/badge/snail-js?style=flat&label=github&labelColor=F56C6C&link=https%3A%2F%2Fgihub.com%2Flimingchang%2Fsnail)
143
342
 
144
-
145
343
  ### 作者
146
- - mc.lee
344
+
345
+ - mc.lee
@@ -2,4 +2,4 @@ import MemoryCache from "./memoryCache";
2
2
  import LocalStorageCache from "./localstorageCache";
3
3
  import IndexDBCache from "./indexDBCache";
4
4
  import { CacheType } from "../typings";
5
- export declare function createCache(type: CacheType, ttl: number): MemoryCache | LocalStorageCache | IndexDBCache | undefined;
5
+ export declare function createCache(type: CacheType, ttl: number): LocalStorageCache | IndexDBCache | MemoryCache | undefined;
@@ -1,2 +1 @@
1
- export * from './api';
2
1
  export * from './snail';
@@ -1,20 +1,28 @@
1
- import { AxiosInstance } from "axios";
2
- import { Api } from "./api";
3
- import { SnailConfig, VersioningConfig, ApiConfig, CacheStorage } from "../typings";
4
- export declare class Snail {
5
- axiosInstance: AxiosInstance;
6
- baseURL: string;
7
- versioning?: VersioningConfig;
8
- options: SnailConfig;
9
- cacheStorage?: CacheStorage;
10
- cacheSource: Api[];
11
- constructor(options: SnailConfig);
12
- Get<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
13
- Post<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
14
- Put<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
15
- Delete<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
16
- Patch<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
17
- Head<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
18
- Options<T = any, E = any, D = any>(url: string, options?: ApiConfig): Api<T, E, D>;
1
+ import "reflect-metadata";
2
+ import { Strategy, ApiProxy, ResponseData } from "../typings";
3
+ export declare class Snail<R extends {
4
+ data: any;
5
+ } = ResponseData> {
6
+ private axiosInstance;
7
+ private strategies;
8
+ private cacheStorage?;
9
+ private version?;
10
+ private sourceMap;
11
+ registerStrategy(strategy: Strategy): void;
12
+ createApi<T extends object>(constructor: new () => T): ApiProxy<T, R>;
13
+ private buildRequestArgs;
14
+ private getStrategies;
15
+ private applyStrategies;
16
+ private applyVersion;
17
+ private getCache;
18
+ private initCacheManage;
19
+ private getHitSource;
20
+ private setHitSource;
21
+ private setCache;
22
+ private expireCache;
23
+ private getServerConfig;
24
+ private getApiConfig;
25
+ private initAxios;
26
+ private handleResponse;
27
+ private handleError;
19
28
  }
20
- export declare const createSnail: (option: SnailConfig) => Snail;
@@ -0,0 +1,12 @@
1
+ import "reflect-metadata";
2
+ import { ApiConfig } from "../typings";
3
+ export declare const METHOD_KEY: unique symbol;
4
+ export declare const API_CONFIG_KEY: unique symbol;
5
+ export declare const Api: (url?: string, config?: ApiConfig) => ClassDecorator;
6
+ export declare const Get: (path?: string) => (target: any, propertyKey: string | symbol) => void;
7
+ export declare const Post: (path?: string) => (target: any, propertyKey: string | symbol) => void;
8
+ export declare const Put: (path?: string) => (target: any, propertyKey: string | symbol) => void;
9
+ export declare const Delete: (path?: string) => (target: any, propertyKey: string | symbol) => void;
10
+ export declare const Patch: (path?: string) => (target: any, propertyKey: string | symbol) => void;
11
+ export declare const Options: (path?: string) => (target: any, propertyKey: string | symbol) => void;
12
+ export declare const Head: (path?: string) => (target: any, propertyKey: string | symbol) => void;
@@ -0,0 +1,3 @@
1
+ import "reflect-metadata";
2
+ export declare const CACHE_OPTIONS_KEY: unique symbol;
3
+ export declare const Cache: (hitSource: string | null) => (target: any, propertyKey?: string) => void;
@@ -0,0 +1,4 @@
1
+ import "reflect-metadata";
2
+ export declare const REQUEST_ARGS_KEY: unique symbol;
3
+ export declare const Params: (key?: string) => (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
4
+ export declare const Data: () => (target: any, propertyKey: string | symbol, parameterIndex: number) => void;
@@ -0,0 +1,4 @@
1
+ import "reflect-metadata";
2
+ import { SnailOption } from "../typings";
3
+ export declare const SERVER_CONFIG_KEY: unique symbol;
4
+ export declare const Server: (config: SnailOption) => (target: any) => void;
@@ -0,0 +1,4 @@
1
+ import "reflect-metadata";
2
+ import { Strategy } from "../typings";
3
+ export declare const STRATEGY_KEY: unique symbol;
4
+ export declare const UseStrategy: (...strategies: Strategy[]) => (target: any, propertyKey?: string) => void;
@@ -0,0 +1,6 @@
1
+ import "reflect-metadata";
2
+ import { VersioningOption } from "../typings";
3
+ export declare const VERSIONING_KEY: unique symbol;
4
+ export declare const VERSION_KEY: unique symbol;
5
+ export declare const Versioning: (options: VersioningOption) => (target: any) => void;
6
+ export declare const Version: (version: string) => (target: any, propertyKey: string) => void;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,9 @@
1
1
  export * from "./core";
2
+ export { Server } from "./decorators/server";
3
+ export { Api, Get, Put, Post, Patch, Options, Head, Delete, } from "./decorators/api";
4
+ export { Cache } from "./decorators/cache";
5
+ export { Params, Data } from "./decorators/param";
6
+ export { UseStrategy } from "./decorators/strategy";
7
+ export { Versioning, Version } from "./decorators/versioning";
2
8
  export * from "./typings";
3
9
  export * from "./utils";