8.6 KiB
8.6 KiB
| name | description |
|---|---|
| repo-core-controller | @repo/core HTTP 控制器装饰器使用指南。当需要创建 REST API 端点、处理 HTTP 请求参数、实现权限控制、或使用 SSE 流式响应时使用此技能。适用于:(1) 使用 @Controller 和 HTTP 方法装饰器(@Get, @Post, @Put, @Delete, @Patch) (2) 使用参数装饰器(@Body, @Query, @Param, @Header) (3) 实现权限和会话验证(@RequirePermissions, @RequireSession) (4) 创建 SSE 端点(@Sse) (5) 添加 API 文档(@ApiDescription) |
@repo/core HTTP 控制器
核心概念
@repo/core 提供声明式 HTTP 控制器装饰器,支持路由定义、参数绑定、权限控制和 SSE 流式响应。
快速开始
创建控制器
import { Controller, Get, Post, Body, Param, Injectable } from '@repo/core'
import { z } from 'zod'
@Controller('/api/users')
class UserController {
constructor(private userService: UserService) {}
@Get('/list')
async getUsers() {
return this.userService.findAll()
}
@Get('/:id')
async getUser(@Param('id') id: string) {
return this.userService.findById(id)
}
@Post('/create', z.object({
name: z.string(),
email: z.string().email()
}))
async createUser(@Body() data: { name: string; email: string }) {
return this.userService.create(data)
}
}
HTTP 方法装饰器
@Get(path)
@Get('/users')
async getUsers() { }
@Get('/users/:id')
async getUser(@Param('id') id: string) { }
@Get('/search')
async search(@Query('q') query: string) { }
@Post(path, schema?, contentType?)
// 基本用法
@Post('/users')
async createUser(@Body() data: CreateUserDto) { }
// 带 Zod 验证
@Post('/users', z.object({
name: z.string().min(1),
email: z.string().email()
}))
async createUser(@Body() data: { name: string; email: string }) { }
// 指定 Content-Type
@Post('/upload', schema, 'multipart/form-data')
async upload(@Body() data: FormData) { }
@Put(path, schema?)
@Put('/users/:id', z.object({
name: z.string().optional(),
email: z.string().email().optional()
}))
async updateUser(
@Param('id') id: string,
@Body() data: Partial<User>
) { }
@Delete(path)
@Delete('/users/:id')
async deleteUser(@Param('id') id: string) { }
@Patch(path, schema?)
@Patch('/users/:id/status', z.object({
status: z.enum(['active', 'inactive'])
}))
async updateStatus(
@Param('id') id: string,
@Body('status') status: string
) { }
@Sse(path) - Server-Sent Events
@Sse('/events')
async *streamEvents() {
while (true) {
yield { data: { timestamp: Date.now() } }
await sleep(1000)
}
}
@Sse('/notifications/:userId')
async *userNotifications(@Param('userId') userId: string) {
const stream = this.notificationService.subscribe(userId)
for await (const notification of stream) {
yield { event: 'notification', data: notification }
}
}
参数装饰器
@Body(key?, schema?)
// 获取整个请求体
@Post('/users')
async create(@Body() data: CreateUserDto) { }
// 获取特定字段
@Post('/users')
async create(@Body('name') name: string) { }
// 带验证
@Post('/users')
async create(@Body('email', z.string().email()) email: string) { }
@Query(key?, schema?)
// 获取查询参数
@Get('/search')
async search(@Query('q') query: string) { }
// 带默认值和验证
@Get('/list')
async list(
@Query('page', z.coerce.number().default(1)) page: number,
@Query('limit', z.coerce.number().default(10)) limit: number
) { }
@Param(key)
@Get('/users/:id')
async getUser(@Param('id') id: string) { }
@Get('/projects/:projectId/tasks/:taskId')
async getTask(
@Param('projectId') projectId: string,
@Param('taskId') taskId: string
) { }
@Header(key)
@Get('/protected')
async protected(@Header('authorization') auth: string) { }
@Post('/webhook')
async webhook(
@Header('x-signature') signature: string,
@Body() payload: any
) { }
@Session()
@Get('/profile')
async getProfile(@Session() session: UserSession) {
return this.userService.findById(session.userId)
}
@Req() / @Res()
@Get('/download')
async download(@Req() req: Request, @Res() res: Response) {
// 直接访问原始请求/响应对象
res.setHeader('Content-Disposition', 'attachment')
return fileStream
}
@Headers()
@Post('/webhook')
async webhook(@Headers() headers: Record<string, string>) {
const signature = headers['x-signature']
// ...
}
权限控制
@RequireSession()
@Controller('/api/account')
class AccountController {
@Get('/profile')
@RequireSession()
async getProfile(@Session() session: UserSession) {
return this.userService.findById(session.userId)
}
}
@RequirePermissions(permissions, mode?)
// 需要单个权限
@Delete('/users/:id')
@RequirePermissions({ user: ['delete'] })
async deleteUser(@Param('id') id: string) { }
// 需要多个权限 (AND 模式 - 全部满足)
@Put('/projects/:id')
@RequirePermissions({ project: ['read', 'update'] }, 'AND')
async updateProject(@Param('id') id: string) { }
// OR 模式 - 满足任一即可
@Get('/reports')
@RequirePermissions({ report: ['read'], admin: ['access'] }, 'OR')
async getReports() { }
权限资源类型
// 可用的权限资源和操作
const permissions = {
activity: ['create', 'read', 'update', 'delete'],
project: ['create', 'read', 'list', 'update', 'delete', 'run'],
template: ['create', 'read', 'list', 'update', 'delete', 'run'],
user: ['read', 'update', 'delete'],
admin: ['access', 'manage'],
report: ['read', 'export']
}
API 文档
@ApiDescription(description, tags?)
@Controller('/api/users')
class UserController {
@Get('/list')
@ApiDescription('获取用户列表', ['Users', 'Admin'])
async getUsers() { }
@Post('/create')
@ApiDescription('创建新用户', ['Users'])
async createUser(@Body() data: CreateUserDto) { }
}
完整示例
import {
Controller, Get, Post, Put, Delete, Sse,
Body, Query, Param, Header, Session,
RequireSession, RequirePermissions, ApiDescription,
Injectable
} from '@repo/core'
import { z } from 'zod'
const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(['user', 'admin']).default('user')
})
const UpdateUserSchema = CreateUserSchema.partial()
@Controller('/api/users')
class UserController {
constructor(
private userService: UserService,
private notificationService: NotificationService
) {}
@Get('/list')
@RequireSession()
@ApiDescription('获取用户列表', ['Users'])
async list(
@Query('page', z.coerce.number().default(1)) page: number,
@Query('limit', z.coerce.number().default(20)) limit: number,
@Query('search') search?: string
) {
return this.userService.findAll({ page, limit, search })
}
@Get('/:id')
@RequireSession()
@ApiDescription('获取用户详情', ['Users'])
async getById(@Param('id') id: string) {
return this.userService.findById(id)
}
@Post('/create', CreateUserSchema)
@RequirePermissions({ user: ['create'] })
@ApiDescription('创建用户', ['Users', 'Admin'])
async create(@Body() data: z.infer<typeof CreateUserSchema>) {
return this.userService.create(data)
}
@Put('/:id', UpdateUserSchema)
@RequirePermissions({ user: ['update'] })
@ApiDescription('更新用户', ['Users', 'Admin'])
async update(
@Param('id') id: string,
@Body() data: z.infer<typeof UpdateUserSchema>
) {
return this.userService.update(id, data)
}
@Delete('/:id')
@RequirePermissions({ user: ['delete'] })
@ApiDescription('删除用户', ['Users', 'Admin'])
async delete(@Param('id') id: string) {
return this.userService.delete(id)
}
@Sse('/notifications')
@RequireSession()
@ApiDescription('用户通知流', ['Users', 'Realtime'])
async *notifications(@Session() session: UserSession) {
const stream = this.notificationService.subscribe(session.userId)
for await (const notification of stream) {
yield { event: 'notification', data: notification }
}
}
}
最佳实践
- 使用 Zod 验证 - 始终为 POST/PUT/PATCH 请求定义 schema
- 分层权限 - 使用 @RequireSession 和 @RequirePermissions 组合
- API 文档 - 为所有端点添加 @ApiDescription
- 参数验证 - 使用 @Query 和 @Body 的 schema 参数
- SSE 清理 - 确保 SSE 生成器正确处理客户端断开
- 控制器单一职责 - 每个控制器处理一个资源类型