NestJS工作原理与装饰器详解
学习目标:理解 NestJS 的完整工作流程,掌握装饰器的原理和参数装饰器的使用。
一、NestJS 工作流程图解
1.1 完整请求流程
code
NestJS 请求处理流程:
│
├── 前端发起请求
│ └── HTTP Request(GET、POST、PUT、DELETE)
│
├── Controller 层(控制器)
│ ├── 接收请求
│ ├── 解析参数
│ ├── 调用 Service
│ └── 返回响应
│
├── Service 层(服务)
│ ├── 业务逻辑处理
│ ├── 数据转换
│ └── 调用 ORM
│
├── ORM 层(对象关系映射)
│ ├── 转换 SQL
│ └── 执行数据库操作
│
├── Database 层(数据库)
│ ├── 执行 SQL
│ ├── 读取/写入数据
│ └── 返回结果
│
└── 响应返回
├── Database → ORM → Service → Controller → 前端
└── Response(JSON、HTML、XML)1.2 各层职责详解
| 层级 | 职责 | 关键词 | 示例 |
|---|---|---|---|
| Controller | 接收请求、解析参数、返回响应 | 路由、参数解析 | @Controller('users') |
| Service | 业务逻辑处理、数据转换 | 业务规则、数据处理 | @Injectable() |
| ORM | 对象关系映射、SQL 转换 | Prisma、TypeORM | prisma.user.findMany() |
| Database | 数据持久化、查询执行 | PostgreSQL、MySQL | SELECT * FROM users |
1.3 RESTful 请求类型
code
RESTful API 请求类型:
│
├── GET
│ ├── 作用:获取资源
│ ├── 示例:GET /users
│ └── 特点:幂等、安全
│
├── POST
│ ├── 作用:创建资源
│ ├── 示例:POST /users
│ └── 特点:非幂等
│
├── PUT
│ ├── 作用:更新资源(整体)
│ ├── 示例:PUT /users/1
│ └── 特点:幂等
│
├── PATCH
│ ├── 作用:更新资源(部分)
│ ├── 示例:PATCH /users/1
│ └── 特点:幂等
│
└── DELETE
├── 作用:删除资源
├── 示例:DELETE /users/1
└── 特点:幂等二、装饰器(Decorators)原理
2.1 什么是装饰器
装饰器定义:
- 装饰器是一个函数
- 用于修改类、方法、属性或参数的行为
- 类似于 Java 的注解(Annotation)
官方定义(TypeScript 文档):
typescript
// 装饰器本质上就是一个函数
function sealed(target: any) {
// target 就是使用装饰器的那个类或方法
console.log('装饰器被调用', target);
}
// 使用装饰器
@sealed
class Person {
name: string;
}等价于:
typescript
// 装饰器会被编译成函数调用
const Person = sealed(class Person {
name: string;
});2.2 装饰器的工作原理
编译前(TypeScript):
typescript
@Controller('users')
export class UserController {
@Get()
findAll() {
return 'This action returns all users';
}
}编译后(JavaScript):
javascript
// 生成的元数据
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc,
d;
for (var i = decorators.length - 1; i >= 0; i--) {
if (d = decorators[i]) {
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
}
}
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// 应用装饰器
UserController = __decorate([
(0, common_1.Controller)('users')
], UserController);
__decorate([
(0, common_1.Get)()
], UserController.prototype, "findAll", null);核心逻辑:
code
装饰器执行过程:
│
├── 第一步:定义装饰器函数
│ └── function Controller(path: string) { ... }
│
├── 第二步:TypeScript 编译器识别装饰器
│ └── 发现 @Controller、@Get 等装饰器
│
├── 第三步:生成元数据(Metadata)
│ ├── __decorate 函数
│ └── Reflect.metadata API
│
└── 第四步:运行时应用装饰器
├── 修改类或方法的行为
└── 附加元数据信息2.3 TypeScript 配置
tsconfig.json:
json
{
"compilerOptions": {
// 启用装饰器功能
"experimentalDecorators": true,
// 启用装饰器元数据
"emitDecoratorMetadata": true,
// 其他配置
"target": "ES2021",
"module": "commonjs",
"strict": true
}
}配置说明:
| 配置项 | 作用 | 必要性 |
|---|---|---|
experimentalDecorators | 启用装饰器功能 | 必须 |
emitDecoratorMetadata | 生成装饰器元数据 | 必须 |
target | 编译目标版本 | 重要 |
module | 模块系统 | 重要 |
为什么需要元数据?
code
元数据的作用:
│
├── 依赖注入
│ ├── 识别构造函数参数类型
│ ├── 自动注入依赖
│ └── 实现控制反转(IoC)
│
├── 路由映射
│ ├── 识别控制器路径
│ ├── 识别请求方法
│ └── 映射到具体处理函数
│
└── 参数解析
├── 识别参数类型
├── 自动提取请求参数
└── 类型转换和验证2.4 四种装饰器类型
1. 类装饰器(Class Decorator)
typescript
// 类装饰器
function Controller(path: string) {
return function (target: any) {
// target 就是类本身
console.log(`Controller path: ${path}`);
console.log(`Class name: ${target.name}`);
};
}
@Controller('users')
class UserController {
// ...
}2. 方法装饰器(Method Decorator)
typescript
// 方法装饰器
function Get(path: string) {
return function (
target: any, // 类的原型
propertyKey: string, // 方法名
descriptor: PropertyDescriptor // 属性描述符
) {
console.log(`GET path: ${path}`);
console.log(`Method name: ${propertyKey}`);
};
}
class UserController {
@Get('')
findAll() {
return 'All users';
}
}3. 属性装饰器(Property Decorator)
typescript
// 属性装饰器
function Inject(token: any) {
return function (target: any, propertyKey: string) {
console.log(`Inject ${token} into ${propertyKey}`);
};
}
class UserController {
@Inject('UserService')
userService: any;
}4. 参数装饰器(Parameter Decorator)
typescript
// 参数装饰器
function Body(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Body parameter at index ${parameterIndex}`);
}
class UserController {
create(@Body() userData: any) {
return userData;
}
}三、NestJS 核心装饰器
3.1 控制器装饰器
@Controller() 装饰器源码:
typescript
// node_modules/@nestjs/common/decorators/core/controller.decorator.ts
import { Path } from '@nestjs/common/interfaces';
export function Controller(): ClassDecorator;
export function Controller(prefix: string): ClassDecorator;
export function Controller(prefix: string[]): ClassDecorator;
export function Controller(prefix?: string | string[]): ClassDecorator {
return (target: object) => {
// 设置元数据
Reflect.defineMetadata(CONTROLLER_WATERMARK, true, target);
Reflect.defineMetadata(PATH_METADATA, prefix, target);
};
}使用示例:
typescript
// 单个路径
@Controller('users')
export class UserController {}
// 多个路径
@Controller(['users', 'members'])
export class UserController {}
// 根路径
@Controller()
export class AppController {}3.2 请求方法装饰器
@Get() 装饰器源码:
typescript
// node_modules/@nestjs/common/decorators/http/request-mapping.decorator.ts
export function Get(path?: string | string[]): MethodDecorator {
return RequestMapping({
path,
method: RequestMethod.GET,
});
}
function RequestMapping(metadata: RequestMappingMetadata): MethodDecorator {
return (target: any, key: string, descriptor: PropertyDescriptor) => {
// 设置路径元数据
Reflect.defineMetadata(PATH_METADATA, metadata.path, descriptor.value);
// 设置方法元数据
Reflect.defineMetadata(METHOD_METADATA, metadata.method, descriptor.value);
};
}常用请求方法装饰器:
typescript
import { Controller, Get, Post, Put, Delete, Patch } from '@nestjs/common';
@Controller('users')
export class UserController {
// GET /users
@Get()
findAll() {}
// GET /users/:id
@Get(':id')
findOne(@Param('id') id: string) {}
// POST /users
@Post()
create(@Body() userData: any) {}
// PUT /users/:id
@Put(':id')
update(@Param('id') id: string, @Body() userData: any) {}
// PATCH /users/:id
@Patch(':id')
partialUpdate(@Param('id') id: string, @Body() userData: any) {}
// DELETE /users/:id
@Delete(':id')
remove(@Param('id') id: string) {}
}四、参数装饰器详解
4.1 参数装饰器总览
必须记住的参数装饰器图:
code
NestJS 参数装饰器速记图:
│
├── @Request() / @Req() → 获取完整请求对象
│ └── 包含:params、query、body、headers 等
│
├── @Query() → 获取查询参数(?name=value)
│ └── 示例:GET /users?name=Tom&age=20
│
├── @Param() → 获取路径参数(/users/:id)
│ └── 示例:GET /users/123
│
├── @Body() → 获取请求体(POST/PUT)
│ └── 示例:POST /users { "name": "Tom" }
│
├── @Headers() → 获取请求头
│ └── 示例:{ authorization: "Bearer xxx" }
│
├── @Ip() → 获取客户端 IP
│ └── 示例:192.168.1.1
│
└── @Session() → 获取 Session 对象
└── 示例:{ userId: 123 }4.2 @Query - 查询参数
获取所有查询参数:
typescript
import { Controller, Get, Query } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get('search')
search(@Query() query: any) {
console.log(query); // { name: 'Tom', age: '20' }
return `Searching for ${query.name}`;
}
}请求示例:
bash
GET /users/search?name=Tom&age=20响应:
json
{
"message": "Searching for Tom"
}获取单个查询参数:
typescript
@Controller('users')
export class UserController {
@Get('search')
search(@Query('name') name: string) {
console.log(name); // Tom
return `Searching for ${name}`;
}
}4.3 @Param - 路径参数
获取所有路径参数:
typescript
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get(':id')
findOne(@Param() params: any) {
console.log(params); // { id: '123' }
return `User ID: ${params.id}`;
}
}请求示例:
bash
GET /users/123响应:
json
{
"message": "User ID: 123"
}获取单个路径参数:
typescript
@Controller('users')
export class UserController {
@Get(':id')
findOne(@Param('id') id: string) {
console.log(id); // 123
return `User ID: ${id}`;
}
}多个路径参数:
typescript
@Controller('users')
export class UserController {
@Get(':userId/posts/:postId')
findPost(
@Param('userId') userId: string,
@Param('postId') postId: string,
) {
return `User ${userId}, Post ${postId}`;
}
}请求示例:
bash
GET /users/123/posts/456响应:
json
{
"message": "User 123, Post 456"
}4.4 @Body - 请求体
获取完整请求体:
typescript
import { Controller, Post, Body } from '@nestjs/common';
@Controller('users')
export class UserController {
@Post()
create(@Body() userData: any) {
console.log(userData); // { name: 'Tom', email: 'tom@example.com' }
return {
message: 'User created',
data: userData,
};
}
}请求示例:
bash
POST /users
Content-Type: application/json
{
"name": "Tom",
"email": "tom@example.com"
}响应:
json
{
"message": "User created",
"data": {
"name": "Tom",
"email": "tom@example.com"
}
}获取单个字段:
typescript
@Controller('users')
export class UserController {
@Post()
create(@Body('name') name: string, @Body('email') email: string) {
console.log(name); // Tom
console.log(email); // tom@example.com
return `User ${name} created with email ${email}`;
}
}使用 DTO(推荐):
typescript
// user.dto.ts
export class CreateUserDto {
name: string;
email: string;
age: number;
}
// user.controller.ts
import { CreateUserDto } from './user.dto';
@Controller('users')
export class UserController {
@Post()
create(@Body() userData: CreateUserDto) {
console.log(userData);
// 类型安全
const { name, email, age } = userData;
return { message: 'User created', data: userData };
}
}4.5 @Headers - 请求头
获取所有请求头:
typescript
import { Controller, Get, Headers } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get('profile')
getProfile(@Headers() headers: any) {
console.log(headers);
// {
// authorization: 'Bearer xxx',
// 'content-type': 'application/json',
// 'user-agent': 'Mozilla/5.0...'
// }
return 'Profile data';
}
}获取单个请求头:
typescript
@Controller('users')
export class UserController {
@Get('profile')
getProfile(@Headers('authorization') token: string) {
console.log(token); // Bearer xxx
return 'Profile data';
}
}4.6 @Request - 完整请求对象
获取完整请求对象:
typescript
import { Controller, Get, Request, Req } from '@nestjs/common';
@Controller('users')
export class UserController {
// 使用 @Request() 或 @Req()
@Get('info')
getInfo(@Request() req: any) {
console.log(req);
// {
// method: 'GET',
// url: '/users/info',
// headers: { ... },
// query: { ... },
// params: { ... },
// body: { ... }
// }
return {
method: req.method,
url: req.url,
query: req.query,
params: req.params,
};
}
}4.7 其他常用装饰器
@Ip() - 客户端 IP:
typescript
import { Controller, Get, Ip } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get('ip')
getClientIp(@Ip() ip: string) {
console.log(ip); // 192.168.1.1
return `Your IP: ${ip}`;
}
}@Session() - Session 对象:
typescript
import { Controller, Get, Session } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get('profile')
getProfile(@Session() session: any) {
console.log(session); // { userId: 123 }
return session;
}
}五、实战示例
5.1 完整的 CRUD 控制器
typescript
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
Headers,
} from '@nestjs/common';
@Controller('users')
export class UserController {
// GET /users?page=1&limit=10
@Get()
findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
) {
return {
message: 'Get all users',
page,
limit,
};
}
// GET /users/:id
@Get(':id')
findOne(@Param('id') id: string) {
return {
message: `Get user ${id}`,
id,
};
}
// POST /users
@Post()
create(
@Body() userData: any,
@Headers('authorization') token: string,
) {
return {
message: 'User created',
data: userData,
token,
};
}
// PUT /users/:id
@Put(':id')
update(
@Param('id') id: string,
@Body() userData: any,
) {
return {
message: `User ${id} updated`,
id,
data: userData,
};
}
// DELETE /users/:id
@Delete(':id')
remove(@Param('id') id: string) {
return {
message: `User ${id} deleted`,
id,
};
}
}5.2 组合使用示例
typescript
import {
Controller,
Get,
Post,
Body,
Param,
Query,
Headers,
Request,
} from '@nestjs/common';
@Controller('api')
export class ApiController {
// 复杂查询
@Get('search')
search(
@Query('keyword') keyword: string,
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
@Headers('authorization') token: string,
) {
return {
message: 'Search results',
keyword,
page,
limit,
authenticated: !!token,
};
}
// 嵌套资源
@Get('users/:userId/posts/:postId')
getUserPost(
@Param('userId') userId: string,
@Param('postId') postId: string,
) {
return {
message: 'User post',
userId,
postId,
};
}
// 完整请求信息
@Post('analyze')
analyzeRequest(
@Request() req: any,
@Body() data: any,
) {
return {
method: req.method,
url: req.url,
headers: req.headers,
query: req.query,
params: req.params,
body: data,
ip: req.ip,
};
}
}六、装饰器最佳实践
6.1 使用 DTO 保证类型安全
定义 DTO:
typescript
// src/user/dto/create-user.dto.ts
export class CreateUserDto {
name: string;
email: string;
age: number;
password: string;
}
// src/user/dto/update-user.dto.ts
export class UpdateUserDto {
name?: string;
email?: string;
age?: number;
}在控制器中使用:
typescript
import { CreateUserDto, UpdateUserDto } from './dto';
@Controller('users')
export class UserController {
@Post()
create(@Body() userData: CreateUserDto) {
// 类型安全,自动提示
const { name, email, age, password } = userData;
return { message: 'User created', data: userData };
}
@Put(':id')
update(
@Param('id') id: string,
@Body() userData: UpdateUserDto,
) {
return { message: `User ${id} updated`, data: userData };
}
}6.2 参数验证
使用 class-validator:
typescript
// src/user/dto/create-user.dto.ts
import { IsString, IsEmail, IsNumber, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
@IsNumber()
age: number;
@IsString()
@MinLength(6)
password: string;
}启用验证:
typescript
// main.ts
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用全局验证管道
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();6.3 参数默认值
typescript
@Controller('users')
export class UserController {
@Get()
findAll(
@Query('page') page: string = '1',
@Query('limit') limit: string = '10',
) {
return {
page: parseInt(page),
limit: parseInt(limit),
};
}
}七、装饰器执行顺序
7.1 装饰器执行顺序
code
装饰器执行顺序(从外到内,从下到上):
│
├── 类装饰器
│ └── @Controller()
│
├── 方法装饰器
│ └── @Get()、@Post()、@Put()、@Delete()
│
└── 参数装饰器
├── @Body()、@Query()、@Param()
└── 从左到右执行示例:
typescript
@Controller('users') // 3. 最后执行
export class UserController {
@Get(':id') // 2. 第二执行
findOne(
@Param('id') id: string, // 1. 先执行(从左到右)
) {
return `User ${id}`;
}
}7.2 参数装饰器执行顺序
typescript
@Post()
create(
@Body() body: any, // 1. 第一个执行
@Headers() headers: any, // 2. 第二个执行
@Ip() ip: string, // 3. 第三个执行
) {
return { body, headers, ip };
}八、学习要点总结
8.1 核心概念速记
code
NestJS 工作原理与装饰器核心概念:
│
├── 工作流程
│ ├── 前端请求 → Controller → Service → ORM → Database
│ └── Database → ORM → Service → Controller → 前端响应
│
├── 装饰器本质
│ ├── 装饰器是一个函数
│ ├── 接收 target 参数(类、方法、属性、参数)
│ └── 通过元数据(Metadata)附加信息
│
├── TypeScript 配置
│ ├── experimentalDecorators: true(启用装饰器)
│ └── emitDecoratorMetadata: true(生成元数据)
│
└── 参数装饰器速记
├── @Query() → 查询参数(?name=value)
├── @Param() → 路径参数(/users/:id)
├── @Body() → 请求体(POST/PUT)
├── @Headers() → 请求头
└── @Request() → 完整请求对象8.2 参数装饰器对比表
| 装饰器 | 获取内容 | 请求示例 | 使用场景 |
|---|---|---|---|
@Query() | 查询参数 | GET /users?name=Tom | 过滤、分页 |
@Param() | 路径参数 | GET /users/:id | 资源标识 |
@Body() | 请求体 | POST /users { "name": "Tom" } | 创建/更新资源 |
@Headers() | 请求头 | Authorization: Bearer xxx | 认证、追踪 |
@Request() | 完整请求 | 包含所有信息 | 复杂场景 |
@Ip() | 客户端 IP | 192.168.1.1 | 日志、限流 |
@Session() | Session | { userId: 123 } | 会话管理 |
8.3 重点知识清单
| 知识点 | 重要程度 | 掌握程度 |
|---|---|---|
| NestJS 请求流程 | 未掌握 / 已掌握 | |
| 装饰器原理 | 未掌握 / 已掌握 | |
| TypeScript 配置 | 未掌握 / 已掌握 | |
| @Query() 使用 | 未掌握 / 已掌握 | |
| @Param() 使用 | 未掌握 / 已掌握 | |
| @Body() 使用 | 未掌握 / 已掌握 | |
| @Headers() 使用 | 未掌握 / 已掌握 | |
| @Request() 使用 | 未掌握 / 已掌握 |
8.4 课后思考题
- NestJS 的请求处理流程是什么?
- 什么是装饰器?它的本质是什么?
- 为什么需要启用
emitDecoratorMetadata? - @Query() 和 @Param() 有什么区别?
- 如何在控制器中获取完整的请求信息?
参考资料
上一章:24-NestJS开发工具扩展
下一章:26-NestJS管道与数据验证