@eggjs/skills 0.0.0 → 4.1.2-beta.4

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.
@@ -0,0 +1,467 @@
1
+ # HTTPController 开发指南
2
+
3
+ ## 快速开始
4
+
5
+ ### 创建基本 HTTP 接口
6
+
7
+ 使用 `@HTTPController` 和 `@HTTPMethod` 装饰器创建 HTTP 接口:
8
+
9
+ ```typescript
10
+ import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam } from 'egg';
11
+
12
+ @HTTPController()
13
+ export class DemoController {
14
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello/:name' })
15
+ async hello(@HTTPParam() name: string) {
16
+ return { message: 'hello ' + name };
17
+ }
18
+ }
19
+ ```
20
+
21
+ ### 设置路径前缀
22
+
23
+ 为控制器设置统一路径前缀:
24
+
25
+ ```typescript
26
+ @HTTPController({ path: '/api' })
27
+ export class PathController {
28
+ // 最终路径: GET /api/hello
29
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: 'hello' })
30
+ async hello() { }
31
+
32
+ // 最终路径: POST /api/create
33
+ @HTTPMethod({ method: HTTPMethodEnum.POST, path: 'create' })
34
+ async create() { }
35
+ }
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 参数装饰器决策
41
+
42
+ ### 决策树:选择合适的参数装饰器
43
+
44
+ ```
45
+ 需要从 HTTP 请求获取什么信息?
46
+
47
+ ├─ URL 路径参数
48
+ │ └─ → @HTTPParam()
49
+
50
+ ├─ URL 查询参数
51
+ │ ├─ 单个值 → @HTTPQuery()
52
+ │ └─ 多个值(数组) → @HTTPQueries()
53
+
54
+ ├─ 请求体(POST/PUT)
55
+ │ └─ → @HTTPBody()
56
+ │ ├─ json → 对象
57
+ │ ├─ text → 字符串
58
+
59
+ ├─ 请求头
60
+ │ └─ → @HTTPHeaders()
61
+ │ └─ 注意:key 自动转小写
62
+
63
+ ├─ Cookie
64
+ │ └─ → @Cookies()
65
+
66
+ ├─ 原始 HTTP 请求对象
67
+ │ └─ → @Request()
68
+ │ └─ 注意:不要和 @HTTPBody 一起消费请求体
69
+
70
+ └─ Egg Context(框架功能)
71
+ └─ → @Context()
72
+ ```
73
+
74
+ ### 参考对照表(参数装饰器)
75
+
76
+ | 装饰器 | 获取内容 | 类型 | 默认值 | 支持选项 |
77
+ | ---------------- | ---------------- | --------------------- | ------ | ------------------- |
78
+ | `@HTTPParam()` | URL 路径参数 | `string` | 变量名 | `{ name?: string }` |
79
+ | `@HTTPQuery()` | 查询参数(单个) | `string` | 变量名 | `{ name?: string }` |
80
+ | `@HTTPQueries()` | 查询参数(多个) | `string[]` | 变量名 | `{ name?: string }` |
81
+ | `@HTTPBody()` | 请求体 | `object \| string` | - | - |
82
+ | `@HTTPHeaders()` | 请求头 | `IncomingHttpHeaders` | - | - |
83
+ | `@Cookies()` | Cookie | `HTTPCookies` | - | - |
84
+ | `@Request()` | HTTP 请求对象 | `HTTPRequest` | - | - |
85
+ | `@Context()` | Egg Context | `EggContext` | - | - |
86
+
87
+ ### 快速选择指南
88
+
89
+ **需要从 URL 获取参数(id)** → `@HTTPParam`
90
+ **需要从查询字符串获取参数(category=books)** → `@HTTPQuery`
91
+ **需要从查询字符串获取全部值(tag=tech&tag=dev)** → `@HTTPQueries`
92
+ **需要获取请求体(POST JSON)** → `@HTTPBody`
93
+ **需要获取请求头字段** → `@HTTPHeaders`(注意:使用小写key)
94
+ **需要读取 Cookie** → `@Cookies`
95
+ **需要访问原始请求对象** → `@Request`
96
+
97
+ ---
98
+
99
+ ## HTTP 响应
100
+
101
+ ### JSON 响应(默认)
102
+
103
+ 直接返回对象,框架自动序列化为 JSON:
104
+
105
+ ```typescript
106
+ @HTTPController({ path: '/api' })
107
+ export class JsonController {
108
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/data' })
109
+ async getData() {
110
+ return { result: 'hello world' };
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### 自定义响应
116
+
117
+ ```typescript
118
+ @HTTPController({ path: '/api' })
119
+ export class ResponseController {
120
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/custom' })
121
+ async customResponse(@Context() ctx: EggContext) {
122
+ ctx.status = 201;
123
+ ctx.set('X-Custom', 'value');
124
+ ctx.type = 'json';
125
+ return { message: 'Created' };
126
+ }
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 服务端渲染(SSR)
133
+
134
+ ```typescript
135
+ @HTTPController({ path: '/' })
136
+ export class SSRController {
137
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/' })
138
+ async render(@Context() ctx: EggContext) {
139
+ ctx.type = 'html';
140
+ return `
141
+ <!DOCTYPE html>
142
+ <html>
143
+ <head><title>Home</title></head>
144
+ <body><h1>Hello World</h1></body>
145
+ </html>
146
+ `;
147
+ }
148
+ }
149
+ ```
150
+
151
+ ---
152
+
153
+ ## 流式响应(Streaming)
154
+
155
+ ```typescript
156
+ import { Readable } from 'node:stream';
157
+ import { setTimeout } from 'node:timers/promises';
158
+
159
+ async function* generateHtml() {
160
+ yield '<html><body>';
161
+ for (let i = 1; i <= 5; i++) {
162
+ yield `<p>Chunk ${i}</p>`;
163
+ await setTimeout(1000);
164
+ }
165
+ yield '</body></html>';
166
+ }
167
+
168
+ @HTTPController({ path: '/api' })
169
+ export class StreamController {
170
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/stream' })
171
+ async streamHtml(@Context() ctx: EggContext) {
172
+ ctx.type = 'html';
173
+ return Readable.from(generateHtml());
174
+ }
175
+ }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 路由优先级管理
181
+
182
+ ### 默认优先级规则
183
+
184
+ | Path | RegExp Index | Priority | 说明 |
185
+ | ------------------------------- | ------------ | -------- | ---------------- |
186
+ | `/*` | `[0]` | 0 | 通配符,最低 |
187
+ | `/hello/:name` | `[1]` | 1000 | 单参数 |
188
+ | `/hello/world/message/:message` | `[3]` | 3000 | 三参数 |
189
+ | `/hello/:name/message/:message` | `[1, 3]` | 4000 | 多参数,索引更大 |
190
+ | `/hello/world` | `[]` | 100000 | 静态路径,最高 |
191
+
192
+ ### 手动设置优先级
193
+
194
+ ```typescript
195
+ @HTTPController({ path: '/api' })
196
+ export class PriorityController {
197
+ @HTTPMethod({
198
+ method: HTTPMethodEnum.GET,
199
+ path: '/(api|openapi)/version',
200
+ priority: 100000, // 提升优先级
201
+ })
202
+ async high() { }
203
+
204
+ @HTTPMethod({
205
+ method: HTTPMethodEnum.POST,
206
+ path: '/(api|openapi)/(.+)',
207
+ })
208
+ async low() { }
209
+ }
210
+ ```
211
+
212
+ ---
213
+
214
+ ## 参数装饰器详解
215
+
216
+ ### @HTTPParam
217
+
218
+ **装饰器类型**:参数装饰器(Parameter Decorator)
219
+
220
+ **使用场景**:从 URL 路径中提取参数
221
+
222
+ **语法**:`@HTTPParam(param?: HTTPParamParams)`
223
+
224
+ #### 快速参考
225
+
226
+ ```typescript
227
+ @HTTPController({ path: '/api/users' })
228
+ export class UserController {
229
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: ':userId/posts/:postId' })
230
+ async getPost(
231
+ @HTTPParam() userId: string,
232
+ @HTTPParam() postId: string
233
+ ) {
234
+ return { userId, postId };
235
+ }
236
+ }
237
+ ```
238
+
239
+ #### 使用要点
240
+
241
+ - 参数类型必须是 `string`
242
+ - 参数名默认和变量名相同
243
+ - 支持正则表达式捕获:`path: '/files/(.*)'`
244
+ - 使用 `{ name: '0' }` 获取正则第一个匹配
245
+ - 支持多参数路径
246
+
247
+ #### 示例
248
+
249
+ ```typescript
250
+ // 路径参数
251
+ @HTTPController({ path: '/api/users' })
252
+ export class UserController {
253
+ // GET /users/:id
254
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: ':id' })
255
+ async getUser(@HTTPParam() id: string) {
256
+ return { userId: id };
257
+ }
258
+
259
+ // GET /users/:userId/posts/:postId
260
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: ':userId/posts/:postId' })
261
+ async getPost(@HTTPParam() userId: string, @HTTPParam() postId: string) {
262
+ return { userId, postId };
263
+ }
264
+
265
+ // 获取正则匹配
266
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/files/(.*)' })
267
+ async getFile(@HTTPParam({ name: '0' }) path: string) {
268
+ return { path };
269
+ }
270
+ }
271
+ ```
272
+
273
+ ---
274
+
275
+ ### @HTTPQuery
276
+
277
+ **装饰器类型**:参数装饰器(Parameter Decorator)
278
+
279
+ **使用场景**:从 URL 查询字符串提取参数(`?key=value`)
280
+
281
+ **语法**:
282
+
283
+ - `@HTTPQuery(param?: HTTPQueryParams)` - 返回首个匹配的值(`string`)
284
+ - `@HTTPQueries(param?: HTTPQueriesParams)` - 返回全部值的数组(`string[]`)
285
+
286
+ #### 快速参考
287
+
288
+ ```typescript
289
+ @HTTPController({ path: '/api/search' })
290
+ export class SearchController {
291
+ // GET /api/search?category=books
292
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/' })
293
+ async searchByCategory(@HTTPQuery() category: string) {
294
+ return { category };
295
+ }
296
+
297
+ // GET /api/search?tag=tech&tag=dev
298
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/' })
299
+ async searchByTags(@HTTPQueries({ name: 'tag' }) tags: string[]) {
300
+ return { tags };
301
+ }
302
+ }
303
+ ```
304
+
305
+ #### 使用要点
306
+
307
+ - `@HTTPQuery`:返回首个匹配的值
308
+ - `@HTTPQueries`:返回全部值的数组
309
+ - 参数名默认与变量名匹配
310
+ - 使用 `{ name: 'key' }` 指定查询参数名
311
+
312
+ ---
313
+
314
+ ### @HTTPBody
315
+
316
+ **装饰器类型**:参数装饰器(Parameter Decorator)
317
+
318
+ **使用场景**:从请求体提取数据(POST/PUT 请求的 body)
319
+
320
+ **语法**:`@HTTPBody()`
321
+
322
+ **类型**:`object | string | FormData`
323
+
324
+ #### 快速参考
325
+
326
+ ```typescript
327
+ @HTTPController({ path: '/api/users' })
328
+ export class UserController {
329
+ @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/' })
330
+ async createUser(@HTTPBody() body: { name: string; email: string }) {
331
+ return { userId: '123', ...body };
332
+ }
333
+ }
334
+ ```
335
+
336
+ #### Content-Type 解析
337
+
338
+ | Content-Type | 解析结果 |
339
+ | ----------------------------------- | --------------- |
340
+ | `application/json` | 对象 `object` |
341
+ | `text/plain` | 字符串 `string` |
342
+ | `application/x-www-form-urlencoded` | 对象 `object` |
343
+
344
+ **注意**:其他类型注入空值,需用 `@Request` 手动处理
345
+
346
+ ---
347
+
348
+ ### @HTTPHeaders
349
+
350
+ **装饰器类型**:参数装饰器(Parameter Decorator)
351
+
352
+ **使用场景**:获取 HTTP 请求头中的字段
353
+
354
+ **语法**:`@HTTPHeaders()`
355
+
356
+ **类型**:`IncomingHttpHeaders`
357
+
358
+ #### 快速参考
359
+
360
+ ```typescript
361
+ @HTTPController({ path: '/api' })
362
+ export class HeaderController {
363
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/info' })
364
+ async getInfo(@HTTPHeaders() headers: IncomingHttpHeaders) {
365
+ const auth = headers['authorization'];
366
+ const custom = headers['x-custom'];
367
+ return { auth, custom };
368
+ }
369
+ }
370
+ ```
371
+
372
+ #### 使用要点
373
+
374
+ - Headers key 会自动转为小写,取值时使用小写字符
375
+ - 获取单个值:`headers['x-custom']`
376
+
377
+ ---
378
+
379
+ ### @Cookies
380
+
381
+ **装饰器类型**:参数装饰器(Parameter Decorator)
382
+
383
+ **使用场景**:从 HTTP Cookie 中读取会话数据
384
+
385
+ **语法**:`@Cookies()`
386
+
387
+ **类型**:`HTTPCookies`
388
+
389
+ #### 快速参考
390
+
391
+ ```typescript
392
+ @HTTPController({ path: '/api' })
393
+ export class SessionController {
394
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/session' })
395
+ async getSession(@Cookies() cookies: HTTPCookies) {
396
+ const session = cookies.get('sessionId');
397
+ return { session };
398
+ }
399
+ }
400
+ ```
401
+
402
+ #### 使用要点
403
+
404
+ - 使用 `cookies.get(key)` 读取 Cookie
405
+ - 使用 `{ signed: false }` 读取未签名的 Cookie
406
+
407
+ ---
408
+
409
+ ### @Request
410
+
411
+ **装饰器类型**:参数装饰器(Parameter Decorator)
412
+
413
+ **使用场景**:访问完整的 HTTP 请求对象
414
+
415
+ **语法**:`@Request()`
416
+
417
+ **类型**:`HTTPRequest`
418
+
419
+ #### 快速参考
420
+
421
+ ```typescript
422
+ @HTTPController({ path: '/api' })
423
+ export class RequestController {
424
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/debug' })
425
+ async getDebug(@Request() request: HTTPRequest) {
426
+ const url = request.url;
427
+ const method = request.method;
428
+ const contentType = request.headers.get('content-type');
429
+ const rawBody = await request.text();
430
+ return { url, method, contentType, bodyLength: rawBody.length };
431
+ }
432
+ }
433
+ ```
434
+
435
+ #### 使用要点
436
+
437
+ - `request.url`:请求 URL
438
+ - `request.method`:请求方法
439
+ - `request.headers`:Headers 对象
440
+ - `request.text()`:读取请求体为文本
441
+ - `request.arrayBuffer()`:读取请求体为 ArrayBuffer
442
+
443
+ ### @Context
444
+
445
+ **装饰器类型**:参数装饰器(Parameter Decorator)
446
+
447
+ **使用场景**:访问 Egg 框架的 Context 对象
448
+
449
+ **语法**:`@Context()`
450
+
451
+ **类型**:`EggContext`
452
+
453
+ #### 快速参考
454
+
455
+ ```typescript
456
+ @HTTPController({ path: '/api' })
457
+ export class DebugController {
458
+ @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/debug' })
459
+ async debug(@Context() ctx: EggContext) {
460
+ return {
461
+ app: ctx.app.name,
462
+ ip: ctx.ip,
463
+ userAgent: ctx.get('user-agent')
464
+ };
465
+ }
466
+ }
467
+ ```