一、引言:前端学 Nest,最绕的就是“怎么传参”
作为前端开发者,我之前对接口的认知很简单:“我用 Axios 发请求,后端给我返回 JSON”。直到开始学 NestJS 写后端,才发现“传参”里藏着好多细节——
- 为什么前端传 URL 里的
/users/123,Nest 要用@Param接? - 为什么传 FormData 文件,Nest 要装
multer? - 为什么传 JSON 的时候,Nest 要加
ValidationPipe?
今天这篇文章,从前端视角出发,把 NestJS 中最常用的 5 种数据传输方式(URL Param、Query、Form-urlencoded、Form-data、JSON)扒得明明白白——前端怎么发请求,Nest 怎么接,踩过的坑全告诉你!
二、先搞懂:Nest 的“参数装饰器”是核心
NestJS 接收前端参数的关键,是参数装饰器——它帮你从请求中“扒”出对应的数据。常见的装饰器有这些:
| 装饰器 | 作用 | 对应前端传法 |
|---|---|---|
@Param() | 取 URL 路径中的参数(比如/users/:id) | URL Param |
@Query() | 取 URL 后的查询参数(比如?page=1) | Query |
@Body() | 取请求体中的数据(比如 JSON、FormData) | JSON/Form-urlencoded |
@UploadedFile() | 取 FormData 中的文件 | Form-data |
记住这些装饰器,后面每个方式都会用到~
三、逐个拆解:5 种方式的“前端发+Nest 接”全流程
3.1. URL Param:最符合 RESTful 的“资源标识”
URL Param 是嵌在 URL 路径里的参数,用来标识唯一资源(比如用户 ID、商品 ID)。比如前端访问/users/123,123就是用户 ID 的 Param。
前端怎么发?(Axios 示例)
直接把参数拼在 URL 里,或者用 Axios 的url占位符(更优雅):
// 方式1:手动拼URL(简单但不够灵活)
axios.get('/users/123')
// 方式2:用Axios的URL占位符(推荐,参数化)
axios.get('/users/:userId', {
params: { userId: 123 }, // Axios自动替换`:userId`为123
})Nest 怎么接?(控制器示例)
用@Param()装饰器取 URL 里的参数,还能给参数起别名(比如@Param('userId') id: number):
// Nest控制器代码(src/users/users.controller.ts)
import { Controller, Get, Param } from '@nestjs/common'
@Controller('users')
export class UsersController {
@Get(':userId') // 对应URL路径`/users/:userId`
findOne(@Param('userId') id: number) {
// 取URL中的`userId`参数,存到`id`变量
return `查询用户ID:${id}` // 返回给前端的响应
}
}适用场景&踩坑提示
- 适用:传资源唯一标识(比如用户 ID、商品 ID),符合 RESTful 规范;
- 踩坑:
- 前端别把敏感信息放 URL 里(比如密码)——URL 会被浏览器缓存、日志记录;
- Nest 里
@Param()的参数名要和 URL 路径的:参数名一致,否则取不到值!
3.2 Query:最常用的“查询条件”
Query 是URL 中?后的键值对,用来传过滤、分页、排序等条件。比如前端访问/users?page=1&size=10,page=1和size=10就是 Query 参数。
前端怎么发?(Axios 示例)
用 Axios 的params配置项,自动转成 Query 字符串:
axios.get('/users', {
params: {
page: 1, // 第1页
size: 10, // 每页10条
status: 'active', // 只查“活跃”用户
},
})
// 最终URL:/users?page=1&size=10&status=activeNest 怎么接?(控制器示例)
用@Query()装饰器取 Query 参数,还能设置默认值(比如@Query('page', new DefaultValuePipe(1)) page: number):
// Nest控制器代码
import { Controller, Get, Query, DefaultValuePipe } from '@nestjs/common'
@Controller('users')
export class UsersController {
@Get() // 对应URL路径`/users`
findAll(
@Query('page', new DefaultValuePipe(1)) page: number, // 取Query中的`page`,默认1
@Query('size', new DefaultValuePipe(10)) size: number, // 取Query中的`size`,默认10
@Query('status') status: string // 取Query中的`status`
) {
return `查询第${page}页,每页${size}条,状态:${status}`
}
}适用场景&踩坑提示
- 适用:传查询条件、分页信息、过滤参数;
- 踩坑:
- Query 参数有长度限制(浏览器一般上限 2k-8k),别传太大的数据(比如长列表);
- Nest 里如果要给 Query 参数加验证(比如
page必须是正数),可以用ValidationPipe+class-validator~
3.3 Form-urlencoded:表单的“传统玩法”
Form-urlencoded 是HTML 表单的默认格式,把数据编码成key=value的键值对(比如username=zhangsan&password=123)。现在虽然用得少,但有些旧系统或第三方接口还需要它。
前端怎么发?
需要手动做两件事:
- 用
qs.stringify()把对象转成key=value字符串(Axios 不自动处理); - 手动设置
Content-Type为application/x-www-form-urlencoded。
(需安装qs库:npm i qs)
import qs from 'qs'
axios.post(
'/login',
qs.stringify({
// 把对象转成`key=value`字符串
username: 'zhangsan',
password: '123456',
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded', // 必须手动设置
},
}
)Nest 怎么接?(控制器示例)
用@Body()装饰器取数据,但要注意:Nest 默认不解析 Form-urlencoded,需要加urlencoded中间件!
首先,在main.ts中启用中间件:
// src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.use(express.urlencoded({ extended: true })) // 解析Form-urlencoded
await app.listen(3000)
}
bootstrap()然后,控制器里用@Body()接:
// src/auth/auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common'
@Controller('auth')
export class AuthController {
@Post('login')
login(@Body() credentials: { username: string; password: string }) {
// 取请求体中的数据
return `登录用户:${credentials.username},密码:${credentials.password}`
}
}适用场景&踩坑提示
- 适用:传简单表单数据(比如登录、注册),兼容旧系统;
- 踩坑:
- 前端必须手动设置
Content-Type,否则 Nest 无法解析; - 不能传复杂数据(比如嵌套对象、数组)——会被转成字符串,Nest 解析后是
[object Object];
- 前端必须手动设置
3.4 Form-data:唯一能传文件的“神器”
Form-data 是专门用来传文件或二进制数据的格式,把数据分成多个“部分(part)”,每个部分对应一个键值对或文件。比如前端传头像+用户名,就用 Form-data。
前端怎么发?(Axios 示例)
用FormData对象构建数据,Axios 会自动设置Content-Type为multipart/form-data(不用手动加):
<!-- HTML:文件选择框 -->
<input
type="file"
id="avatarInput"
accept="image/*"
/>// 前端JS:构建FormData
const avatarInput = document.getElementById('avatarInput')
const formData = new FormData()
// 传普通键值对(用户名)
formData.append('username', 'zhangsan')
// 传文件(`avatar`是后端接收的字段名,`avatarInput.files[0]`是选中的文件)
formData.append('avatar', avatarInput.files[0])
// 发送请求(Axios自动处理Content-Type)
axios.post('/upload', formData)Nest 怎么接?(控制器示例)
需要用**multer中间件**解析文件,Nest 提供了@nestjs/platform-express的MulterModule来简化配置~
步骤 1:安装依赖:
npm i @nestjs/platform-express multer
npm i -D @types/multer步骤 2:在模块中配置MulterModule(比如AppModule):
// src/app.module.ts
import { Module } from '@nestjs/common'
import { MulterModule } from '@nestjs/platform-express'
import { diskStorage } from 'multer'
import { extname } from 'path'
@Module({
imports: [
MulterModule.register({
storage: diskStorage({
destination: './uploads', // 文件保存路径(需手动创建`uploads`目录)
filename: (req, file, cb) => {
// 生成唯一文件名(避免重名)
const randomName = Array(32)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('')
cb(null, `${randomName}${extname(file.originalname)}`)
},
}),
}),
],
})
export class AppModule {}步骤 3:控制器里用@UploadedFile()接文件:
// src/upload/upload.controller.ts
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common'
import { FileInterceptor } from '@nestjs/platform-express'
@Controller('upload')
export class UploadController {
@Post('avatar')
@UseInterceptors(FileInterceptor('avatar')) // `avatar`是前端FormData的字段名
uploadAvatar(@UploadedFile() file: Express.Multer.File) {
// 取上传的文件
return {
filename: file.filename, // 保存后的文件名
path: file.path, // 文件路径
size: file.size, // 文件大小(字节)
}
}
}适用场景&踩坑提示
- 适用:传文件、二进制数据(比如头像、图片、视频);
- 踩坑:
- 前端别把文件放到 JSON 里传——JSON 是文本格式,无法处理二进制;
- Nest 里如果要限制文件类型(比如只允许图片),可以在
MulterModule配置fileFilter~
3.5 JSON:前端最爱的“复杂数据玩法”
JSON 是现在最常用的格式,支持复杂数据结构(比如嵌套对象、数组)。前端用 Axios 传 JSON,Nest 用@Body()接,完美配合~
前端怎么发?(Axios 示例)
最简单!直接传对象,Axios 会自动序列化成 JSON 字符串,并自动设置Content-Type为application/json:
axios.post('/users', {
name: '张三',
age: 18,
hobbies: ['coding', 'reading'], // 数组
address: {
// 嵌套对象
city: '北京',
street: '朝阳区',
},
})③ Nest 怎么接?(控制器示例)
用@Body()装饰器取数据,如果要加数据验证(比如name必填、age必须是正数),可以用ValidationPipe + class-validator~
步骤 1:安装验证依赖:
npm i class-validator class-transformer步骤 2:在main.ts中启用ValidationPipe:
// src/main.ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe } from '@nestjs/common'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.useGlobalPipes(new ValidationPipe()) // 全局启用验证管道
await app.listen(3000)
}
bootstrap()步骤 3:定义 DTO(数据传输对象),用class-validator加验证规则:
// src/users/dto/create-user.dto.ts
import { IsString, IsNumber, IsArray, IsObject } from 'class-validator'
export class CreateUserDto {
@IsString() // name必须是字符串
name: string
@IsNumber() // age必须是数字
age: number
@IsArray() // hobbies必须是数组
hobbies: string[]
@IsObject() // address必须是对象
address: {
city: string
street: string
}
}步骤 4:控制器里用@Body()接 DTO:
// src/users/users.controller.ts
import { Controller, Post, Body } from '@nestjs/common'
import { CreateUserDto } from './dto/create-user.dto'
@Controller('users')
export class UsersController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
// 取JSON请求体,自动验证
return `创建用户:${createUserDto.name},年龄:${createUserDto.age}`
}
}适用场景&踩坑提示
- 适用:传复杂数据结构(嵌套对象、数组),是现在90%场景的首选;
- 踩坑:
- 后端如果没启用
ValidationPipe,createUserDto里的验证规则不会生效; - JSON 不能传文件,传文件还是得用 Form-data ~
- 后端如果没启用
四、对比总结:5 种方式的“选用决策树”
直接按场景选,不用再纠结:
| 场景 | 推荐方式 | 前端传法 | Nest 接发 |
|---|---|---|---|
| 传资源唯一标识(用户 ID) | URL Param | Axios 拼 URL 或占位符 | @Param() |
| 传查询条件(分页、过滤) | Query | Axios 的params | @Query() |
| 传简单表单(登录、注册) | Form-urlencoded | qs.stringify()+手动设置 Content-Type | @Body()+urlencoded中间件 |
| 传文件/二进制数据 | Form-data | FormData对象 | @UploadedFile()+MulterModule |
| 传复杂数据(嵌套对象) | JSON | 直接传对象 | @Body()+ValidationPipe |
五、前端学 Nest 的“踩坑总结”
这些坑都是我学 Nest 时亲手踩过的,亲测有效避坑!
- 传 JSON 时,Nest 要启用
ValidationPipe——否则 DTO 里的@IsString()、@IsNumber()这些验证规则不会生效,脏数据直接进数据库; - 传 Form-data 时,Nest 要配置
MulterModule——否则@UploadedFile()会报错“Unexpected field”,根本拿不到文件; - URL Param 的参数名要“严丝合缝”——比如前端传
/users/:userId,Nest 的@Param('userId')里的userId必须和路径的:userId完全一致,差一个字母都取不到值(别问我怎么知道的,我之前把userId写成userid,查了半小时才发现); - Query 参数加默认值要用
DefaultValuePipe——比如@Query('page', new DefaultValuePipe(1)),否则没传page时,page会是undefined,前端得处理NaN的情况; - Form-urlencoded 要手动设置
Content-Type——Axios 不会自动加这个头,没设置的话 Nest 的req.body会是空对象,后端根本拿不到数据;
六、写在最后:前端学 Nest 的“小 Tips”
作为前端开发者,学 NestJS 其实是**“换个角度理解接口”**——以前只关心“怎么发请求”,现在要关心“怎么接请求”。分享几个我自己的学习技巧:
- 先啃“参数装饰器”:
@Param()、@Query()、@Body()是基础中的基础,搞懂这三个,80%的接口场景都能覆盖; - 验证用
class-validator:前端传 JSON 时,用 DTO(数据传输对象)加验证规则,能帮你挡住 90%的脏数据,比后端写 if-else 舒服 100 倍; - 文件上传“先配再用”:别上来就写
@UploadedFile(),先把MulterModule的存储路径、文件名规则配置好,再写控制器,否则会踩“文件存不进去”“文件名重复”的坑; - 多写“最小 Demo”:比如每个传输方式写一个单独的小 Demo(比如“URL Param Demo”“Form-data Demo”),跑通了再整合到项目里,比直接写大项目踩坑少得多;
七、结尾:前端学 Nest,没那么难!
一开始学 Nest 时,我也觉得“参数装饰器好复杂”“文件上传好麻烦”,但把这 5 种方式拆开来,一个一个搞懂,突然就“通了”——原来 Nest 的接口设计,本质是**“帮你把前端的请求,按规则‘拆’成能用的数据”**。
作为前端开发者,我们有天然的优势:懂 Axios 怎么发请求,懂 JSON 怎么传,懂 FormData 怎么用——这些优势能帮我们更快理解 Nest 的逻辑。
最后送你一句话:“先搞懂‘怎么传’,再搞懂‘怎么接’,NestJS 其实很友好”。
如果还有疑问,欢迎在评论区留言,我会第一时间解答!一起加油,从“前端”变“全栈”~ 😊
附录:常用依赖安装命令
- 安装
qs(Form-urlencoded 用):npm i qs - 安装
multer(Form-data 用):npm i @nestjs/platform-express multer && npm i -D @types/multer - 安装验证依赖(JSON 用):
npm i class-validator class-transformer
(注:文中代码基于 NestJS v10.x、Axios v1.x,不同版本可能有细微差异,建议用nest --version检查当前版本~)
- 本文链接:https://fridolph.top/posts/2025-09-23__nest05-param
- 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 许可协议。