Appearance
NestJS控制器
控制器负责处理传入的请求并将响应返回给客户端。控制器定义了应用程序的路由,这些路由告诉应用程序如何响应客户端的请求(例如,通过特定的URL路径或HTTP方法调用)。
控制器基础
控制器定义
使用@Controller()装饰器定义控制器:
typescript
import { Controller, Get, Post, Put, Delete, Param, Body, Query } from '@nestjs/common';
@Controller('cats') // 定义路由前缀
export class CatsController {
@Get() // GET /cats
findAll(): string {
return 'This action returns all cats';
}
@Get(':id') // GET /cats/:id
findOne(@Param('id') id: string): string {
return `This action returns a #${id} cat`;
}
@Post() // POST /cats
create(@Body() createCatDto: any): string {
return 'This action adds a new cat';
}
@Put(':id') // PUT /cats/:id
update(@Param('id') id: string, @Body() updateCatDto: any): string {
return `This action updates a #${id} cat`;
}
@Delete(':id') // DELETE /cats/:id
remove(@Param('id') id: string): string {
return `This action removes a #${id} cat`;
}
}
控制器元数据
typescript
import { Controller, Get, Header, HttpCode, HttpStatus, Redirect } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
@Header('Cache-Control', 'none') // 设置响应头
@HttpCode(HttpStatus.OK) // 设置HTTP状态码
findAll(): string {
return 'This action returns all cats';
}
@Get('docs')
@Redirect('https://docs.nestjs.com', 302) // 重定向
getDocs(@Query('version') version: string) {
if (version && version === '5') {
return { url: 'https://docs.nestjs.com/v5/' }; // 动态重定向
}
}
}
路由处理
HTTP方法装饰器
typescript
import {
Controller,
Get,
Post,
Put,
Delete,
Patch,
Head,
Options,
All
} from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get() // GET请求
findAll(): string {
return 'Get all users';
}
@Post() // POST请求
create(): string {
return 'Create user';
}
@Put(':id') // PUT请求
update(@Param('id') id: string): string {
return `Update user ${id}`;
}
@Delete(':id') // DELETE请求
remove(@Param('id') id: string): string {
return `Delete user ${id}`;
}
@Patch(':id') // PATCH请求
partialUpdate(@Param('id') id: string): string {
return `Partially update user ${id}`;
}
@Head(':id') // HEAD请求
head(@Param('id') id: string): void {
// 只返回头部信息
}
@Options(':id') // OPTIONS请求
options(@Param('id') id: string): string {
return `Options for user ${id}`;
}
@All(':id') // 所有HTTP方法
handleAll(@Param('id') id: string): string {
return `Handle all methods for user ${id}`;
}
}
路由参数和查询参数
typescript
import { Controller, Get, Post, Param, Query, Body, Headers, Ip, HostParam } from '@nestjs/common';
@Controller('users')
export class UsersController {
// 路由参数
@Get(':id')
findOne(
@Param('id') id: string,
@Headers('authorization') auth: string,
@Ip() ip: string,
): string {
return `User ID: ${id}, Auth: ${auth}, IP: ${ip}`;
}
// 多个路由参数
@Get(':userId/posts/:postId')
getPost(
@Param('userId') userId: string,
@Param('postId') postId: string,
): string {
return `User ${userId}, Post ${postId}`;
}
// 解构路由参数
@Get(':id')
getWithDestructuring(@Param() params: any): string {
return `ID: ${params.id}`;
}
// 查询参数
@Get()
findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
@Query() queries: any, // 所有查询参数
): string {
return `Page: ${page}, Limit: ${limit}, All: ${JSON.stringify(queries)}`;
}
// 带默认值的查询参数
@Get('search')
search(
@Query('q') query: string,
@Query('sort', new ParseIntPipe({ optional: true })) sort = 1,
): string {
return `Search: ${query}, Sort: ${sort}`;
}
}
请求处理
请求体处理
typescript
import { Controller, Post, Put, Body, UsePipes, ValidationPipe } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
@Controller('cats')
export class CatsController {
// 简单请求体
@Post()
create(@Body() body: any): string {
return `Create cat with data: ${JSON.stringify(body)}`;
}
// 使用DTO
@Post()
createWithDto(@Body() createCatDto: CreateCatDto): string {
return `Create cat: ${createCatDto.name}, ${createCatDto.age}`;
}
// 使用验证管道
@Post()
@UsePipes(new ValidationPipe())
createWithValidation(@Body() createCatDto: CreateCatDto): string {
return `Create cat with validation: ${createCatDto.name}`;
}
// 部分更新
@Put(':id')
update(
@Param('id') id: string,
@Body() updateCatDto: UpdateCatDto,
): string {
return `Update cat ${id} with: ${JSON.stringify(updateCatDto)}`;
}
// 处理嵌套对象
@Post('complex')
createComplex(@Body('user') user: any, @Body('settings') settings: any): string {
return `User: ${user.name}, Settings: ${settings.theme}`;
}
}
文件上传
typescript
import {
Controller,
Post,
UseInterceptors,
UploadedFile,
UploadedFiles
} from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
@Post('single')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
return {
filename: file.filename,
originalName: file.originalname,
size: file.size,
};
}
@Post('multiple')
@UseInterceptors(FilesInterceptor('files', 10)) // 最多10个文件
uploadMultipleFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
return {
fileCount: files.length,
files: files.map(file => ({
filename: file.filename,
originalName: file.originalname,
size: file.size,
})),
};
}
}
响应处理
响应格式
typescript
import {
Controller,
Get,
Post,
Put,
Delete,
Res,
HttpStatus,
Render,
Header,
StreamableFile
} from '@nestjs/common';
import { Response } from 'express';
import { createReadStream } from 'fs';
import { join } from 'path';
@Controller('cats')
export class CatsController {
// JSON响应
@Get(':id')
findOne(@Param('id') id: string): any {
return {
id,
name: 'Kitty',
age: 3,
};
}
// 使用原生响应对象
@Get('stream/:id')
stream(@Param('id') id: string, @Res() res: Response) {
res.status(HttpStatus.OK).json({
id,
name: 'Kitty',
});
}
// 模板渲染
@Get(':id')
@Render('cat') // 渲染模板
getCat(@Param('id') id: string) {
return { id, name: 'Kitty' };
}
// 设置响应头
@Get('headers')
@Header('Content-Type', 'application/json')
@Header('Custom-Header', 'value')
withHeaders(): any {
return { message: 'Headers set' };
}
// 流式文件响应
@Get('file')
getFile(): StreamableFile {
const file = createReadStream(join(process.cwd(), 'files/document.pdf'));
return new StreamableFile(file);
}
// 自定义响应
@Get('custom')
customResponse(@Res({ passthrough: true }) res: Response) {
res.status(HttpStatus.OK);
return { message: 'Custom response' };
}
}
控制器装饰器
方法级装饰器
typescript
import {
Controller,
Get,
UseGuards,
UseInterceptors,
UseFilters,
UsePipes,
SerializeOptions
} from '@nestjs/common';
import { RolesGuard } from '../guards/roles.guard';
import { LoggingInterceptor } from '../interceptors/logging.interceptor';
import { HttpExceptionFilter } from '../filters/http-exception.filter';
import { ValidationPipe } from '../pipes/validation.pipe';
@Controller('admin')
@UseGuards(RolesGuard) // 类级别守卫
@UseInterceptors(LoggingInterceptor) // 类级别拦截器
export class AdminController {
@Get('users')
@UseGuards(RolesGuard) // 方法级别守卫
@UsePipes(ValidationPipe) // 方法级别管道
@UseFilters(HttpExceptionFilter) // 方法级别过滤器
getUsers(): string {
return 'Admin users';
}
@Get('settings')
@SerializeOptions({ strategy: 'excludeAll' }) // 序列化选项
getSettings(): any {
return { secret: 'secret', public: 'public' };
}
}
路由通配符
typescript
import { Controller, Get, All } from '@nestjs/common';
@Controller('files')
export class FilesController {
// 路径模式匹配
@Get('ab*cd') // 匹配 abcd, ab_cd, abecd 等
wildCard(): string {
return 'Pattern matched';
}
@Get('changelog') // 精确匹配
changelog(): string {
return 'Changelog';
}
@Get('file/*') // 匹配 /file/ 后的任何路径
catchAll(): string {
return 'Catch all';
}
@All('*') // 捕获所有未匹配的路由
notFound(): string {
return 'Not found';
}
}
中间件和控制器
控制器级中间件
typescript
import {
Module,
NestModule,
MiddlewareConsumer,
RequestMethod
} from '@nestjs/common';
import { LoggerMiddleware } from './middleware/logger.middleware';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class CatsModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(
{ path: 'cats', method: RequestMethod.ALL },
{ path: 'cats/:id', method: RequestMethod.GET },
);
}
}
路由分组
typescript
import { Controller, Get, Post, Put, Delete } from '@nestjs/common';
@Controller('api/v1/users')
export class UsersV1Controller {
@Get() // GET /api/v1/users
findAll(): string {
return 'Get all users (v1)';
}
@Get(':id') // GET /api/v1/users/:id
findOne(@Param('id') id: string): string {
return `Get user ${id} (v1)`;
}
@Post() // POST /api/v1/users
create(): string {
return 'Create user (v1)';
}
@Put(':id') // PUT /api/v1/users/:id
update(@Param('id') id: string): string {
return `Update user ${id} (v1)`;
}
@Delete(':id') // DELETE /api/v1/users/:id
remove(@Param('id') id: string): string {
return `Remove user ${id} (v1)`;
}
}
控制器最佳实践
1. 控制器职责单一
typescript
// 好的控制器设计 - 职责单一
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Get()
findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string): Promise<Cat> {
return this.catsService.findOne(id);
}
@Post()
create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}
// 避免控制器过于复杂
@Controller('complex')
export class ComplexController {
// 避免在一个控制器中处理过多逻辑
}
2. 使用DTO进行数据验证
typescript
// dto/create-cat.dto.ts
import { IsString, IsInt, Min, Max } from 'class-validator';
export class CreateCatDto {
@IsString()
name: string;
@IsInt()
@Min(0)
@Max(30)
age: number;
@IsString()
breed: string;
}
// 控制器中使用
@Controller('cats')
export class CatsController {
@Post()
@UsePipes(new ValidationPipe())
create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}
3. 合理使用装饰器
typescript
@Controller('users')
export class UsersController {
@Get()
@HttpCode(HttpStatus.OK)
@Header('Cache-Control', 'max-age=3600')
@UseInterceptors(CacheInterceptor) // 使用缓存
findAll(): Promise<User[]> {
return this.usersService.findAll();
}
@Get(':id')
@UseGuards(AuthGuard, RolesGuard) // 多个守卫
findOne(@Param('id') id: string): Promise<User> {
return this.usersService.findOne(id);
}
}
4. 错误处理
typescript
import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get(':id')
async findOne(@Param('id') id: string) {
try {
const cat = await this.catsService.findOne(id);
if (!cat) {
throw new HttpException('Cat not found', HttpStatus.NOT_FOUND);
}
return cat;
} catch (error) {
if (error instanceof HttpException) {
throw error;
}
throw new HttpException(
'Internal server error',
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
通过合理使用控制器,可以构建清晰、可维护的API端点。