@eggjs/skills 0.0.0 → 4.1.2-beta.5
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/PLAN.md +396 -0
- package/egg/SKILL.md +230 -0
- package/egg-controller/SKILL.md +75 -0
- package/egg-controller/references/http-controller.md +467 -0
- package/egg-controller/references/mcp-controller.md +289 -0
- package/egg-controller/references/schedule.md +110 -0
- package/egg-core/SKILL.md +192 -0
- package/egg-core/references/dynamic-inject.md +91 -0
- package/egg-core/references/inject.md +258 -0
- package/egg-core/references/module.md +202 -0
- package/egg-core/references/proto.md +181 -0
- package/package.json +15 -1
|
@@ -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
|
+
```
|