控制器
控制器是您 API 的入口点。它们定义 HTTP 端点并通过提取数据、调用业务逻辑和返回响应来处理传入请求。
概述
在 YasuiJS 中,控制器是用 @Controller() 装饰的类,用于将相关端点组合在一起。控制器中的每个方法都代表一个 HTTP 端点,使用方法装饰器如 @Get()、@Post() 等来定义。
控制器方法可以简单地返回任何值,这些值将自动序列化为 JSON 并返回 200 状态码。如需手动控制响应,您可以直接返回 Web 标准 Response 对象。
import { Controller, Get, Post } from 'yasui';
@Controller('/api/users')
export class UserController {
@Get('/')
getAllUsers() {
return { users: [] }; // 自动返回 JSON
}
}控制器装饰器
@Controller() 装饰器将类标记为控制器并定义其所有路由的基础路径。
基本用法
@Controller('/api/users')
export class UserController {
// 所有路由都将以 /api/users 为前缀
}使用中间件
您可以将中间件应用于控制器中的所有路由。在中间件中了解更多。
@Controller('/api/users', AuthMiddleware)
export class UserController {
// 所有路由都将应用 AuthMiddleware
}HTTP 方法装饰器
YasuiJS 为所有标准 HTTP 方法提供装饰器。每个装饰器接受一个路径参数(必需)和可选的中间件参数。
@Get(path, ...middlewares)- 处理 GET 请求@Post(path, ...middlewares)- 处理 POST 请求@Put(path, ...middlewares)- 处理 PUT 请求@Delete(path, ...middlewares)- 处理 DELETE 请求@Patch(path, ...middlewares)- 处理 PATCH 请求
基本路由
@Controller('/api/users')
export class UserController {
@Get('/')
getAllUsers() {
return { users: [] };
}
@Post('/')
createUser() {
return { message: 'User created' };
}
@Get('/:id')
getUser() {
// 在路径中使用 Express 风格的路由参数:
// 路由:GET /api/users/123
return { user: {} };
}
@Put('/:id')
updateUser() {
return { message: 'User updated' };
}
@Delete('/:id')
deleteUser() {
return { message: 'User deleted' };
}
}路由级中间件
将中间件应用于特定路由。在中间件中了解更多。
@Controller('/api/users')
export class UserController {
@Get('/', ValidationMiddleware)
getAllUsers() {}
}参数装饰器
使用参数装饰器从 HTTP 请求中提取数据。YasuiJS 根据 TypeScript 类型自动转换参数,以获得更好的类型安全性。
提取请求体
@Body(name?) - 提取请求体数据
@Controller('/api/users')
export class UserController {
@Post('/')
createUser(@Body() userData: any) {
// 提取整个请求体
return { created: userData };
}
@Post('/partial')
updateUser(@Body('name') name: string) {
// 从请求体中提取特定字段
return { updatedName: name };
}
}提取参数和头部
@Param(name, items?)- 提取路由参数@Query(name, items?)- 提取查询参数@Header(name, items?)- 提取请求头
参数会根据其 TypeScript 类型自动转换。对于非字符串类型的数组,您必须将项目类型指定为第二个参数:
@Controller('/api/users')
export class UserController {
@Get('/:id')
getUser(@Param('id') id: number) {} // 转换为数字
@Get('/search/:term')
searchUsers(
@Param('term') term: string,
@Header('x-api-version') version: number,
@Query('filters', [Boolean]) filters: boolean[],
@Query('settings') settings: { theme: string } | null,
) {
// version: number(头部转换为数字)
// filters: boolean[](来自 ?filters=true&filters=false&filters=1)
// settings: object(来自 ?settings={"theme":"dark"} - JSON 解析,失败时为 null)
return { page, active, tags, priorities };
}
}自动参数类型转换
YasuiJS 根据 TypeScript 类型自动转换参数:
基本类型
- string - 无转换(默认)
- boolean - 将 "true"/"1" 转换为 true,其他所有内容转换为 false
- number - 转换为数字,无效时返回
NaN(或使用strictValidation时抛出) - Date - 转换为 Date 对象,无效时返回
Invalid Date(或抛出 ``) - object - 解析 JSON 字符串,用于像
?data={"key":"value"}这样的查询,失败时返回null(或抛出 ``)
数组类型
TypeScript 无法在运行时检测数组项类型,因此您必须为非字符串数组指定 [Type]:
- string[] - 不需要额外配置(默认行为)
- number、boolean 或 Date 数组 - 必须使用第二个参数指定项目类型
类型化数组语法:
@Query('paramName', [Type]) paramName: Type[]
@Param('paramName', [Type]) paramName: Type[]
@Header('headerName', [Type]) headerName: Type[]严格验证模式
默认情况下,YasuiJS 在类型转换失败时返回无效值(NaN、Invalid Date、null)。在配置中启用 strictValidation 以抛出 HTTP 400 错误:
yasui.createServer({
controllers: [UserController],
strictValidation: true // 类型转换失败时抛出错误
});默认行为(strictValidation: false):
@Get('/:id')
getUser(@Param('id') id: number) {
// GET /user/abc → id = NaN(静默失败)
// GET /user/123 → id = 123
}
@Get('/search')
search(@Query('date') date: Date) {
// GET /search?date=invalid → date = Invalid Date
// GET /search?date=2024-01-01 → date = Date 对象
}严格验证(strictValidation: true):
@Get('/:id')
getUser(@Param('id') id: number) {
// GET /user/abc → 抛出 HttpError(400, "Parameter 'id' expected number, got 'abc'")
// GET /user/123 → id = 123
}
@Get('/search')
search(
@Query('date') date: Date,
@Query('ids', [Number]) ids: number[]
) {
// GET /search?date=invalid
// → 抛出 HttpError(400, "Parameter 'date' expected valid date, got 'invalid'")
// GET /search?ids=1&ids=2&ids=abc
// → 抛出 HttpError(400, "Parameter 'ids[2]' expected number, got 'abc'")
}JSON body 解析:
@Post('/')
createUser(@Body() data: any) {
// strictValidation: false
// 无效 JSON → data = undefined(静默失败)
// strictValidation: true
// 无效 JSON → 抛出 HttpError(400, "Failed to parse JSON body: ...")
}参见 配置 获取更多详情。
访问请求对象
@Req() - 访问 YasuiJS 请求对象(具有 Express 兼容属性的 Web 标准请求)
import { Request } from 'yasui';
@Controller('/api/users')
export class UserController {
@Get('/')
getAllUsers(@Req() request: Request) {
console.log(request.url);
return { users: [] };
}
}可用的请求属性
url- 完整的请求 URLmethod- HTTP 方法(GET、POST 等)headers/rawHeaders- 请求头(参见访问请求头)params- 路由参数query- 查询字符串参数cookies- 解析的 cookiesbody- 解析的请求体path- 请求路径名hostname- 请求主机名protocol- 请求协议(http/https)ip- 客户端 IP 地址
访问请求头
YasuiJS 提供两种访问请求头的方式:
Express 风格(普通对象)
为了兼容性,避免从 v3 的破坏性变更。
@Get('/')
getUsers(@Req() req: Request) {
const auth = req.headers.authorization; // 点符号
const type = req.headers['content-type']; // 括号符号
}原生 Web 标准(Headers 对象):
@Get('/')
getUsers(@Req() req: Request) {
const auth = req.rawHeaders.get('authorization');
const type = req.rawHeaders.get('content-type');
}何时使用:
req.headers- 当访问多个头部或更喜欢 Express 风格语法时req.rawHeaders- 更适合单个头部检查,性能更好(无对象转换)
创建自定义请求装饰器
您可以使用 routeRequestParamDecorator 创建自己的装饰器来提取请求对象的特定属性。
import { routeRequestParamDecorator } from 'yasui';
// 为请求 IP 创建自定义装饰器
export const Ip = routeRequestParamDecorator('ip');
// 在控制器中使用
@Controller('/api/users')
export class UserController {
@Get('/')
getUsers(@Ip() ip: string) {
console.log(`来自 ${ip} 的请求`);
return { users: [] };
}
}这种方法优于使用 @Req() 访问单个属性,因为它:
- 提高代码可读性
- 启用类型安全
查看访问请求对象获取可用请求属性的完整列表。
响应处理
YasuiJS 自动处理响应序列化和状态码。
自动 JSON 响应
返回任何数据,它将自动序列化为 JSON:
@Controller('/api/users')
export class UserController {
@Get('/')
getUsers() {
// 自动返回状态为 200 的 JSON
return { users: ['John', 'Jane'] };
}
@Get('/string')
getString() {
// 将字符串作为 JSON 返回
return 'Hello World';
}
@Get('/number')
getNumber() {
// 将数字作为 JSON 返回
return 42;
}
}自定义状态码
@HttpStatus(code) - 设置自定义 HTTP 状态码
使用 @HttpStatus() 装饰器设置自定义状态码:
import { HttpStatus, HttpCode } from 'yasui';
@Controller('/api/users')
export class UserController {
@Post('/alt')
@HttpStatus(201) // 使用数字
createUserAlt(@Body() userData: any) {
// 返回状态 201 Created
return { created: userData };
}
@Delete('/:id')
@HttpStatus(HttpCode.NO_CONTENT) // 使用 HttpCode 枚举
deleteUser(@Param('id') id: string) {
// 返回状态 204 No Content
// 对于 204 可以不返回任何内容
}
}手动响应处理
默认情况下,所有返回值都会自动序列化为 JSON。要返回非 JSON 格式(HTML、XML、文件等),必须返回带有适当 Content-Type 头的 Web 标准 Response 对象:
@Controller('/api/users')
export class UserController {
@Get('/custom')
customResponse() {
return new Response(JSON.stringify({
message: "I'm a teapot",
custom: true
}), {
status: 418,
headers: { 'Content-Type': 'application/json' }
});
}
@Get('/download')
downloadFile() {
const fileBuffer = new Uint8Array([/* 文件数据 */]);
return new Response(fileBuffer, {
status: 200,
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="report.pdf"'
}
});
}
}错误处理
让框架自动处理错误或抛出自定义错误。有关完整的错误处理详细信息,请参阅错误处理。