REST API 数据传输
通过 URL
最基本的通过 url 传输形式是这样的:
js1http://localhost/user/:id
通过以下命令,我们先创建一个 user 的 controller
bash1nest generate controller user
通过@Get(':id')来声明路由 以及通过@Param(参数名)装饰器取出参数注入到 controller:
js1@Controller('user') 2export class UserController { 3 @Get(':id') 4 urlParam(@Param('id') id: string) { 5 return `received: id=${id}` 6 } 7}
现在@Controller('user')和@Get(':id')会拼接成为/user/:id这样的 URL。
执行pnpm start:dev命令,我们启动 nest server,默认的端口号为 3000,这些都是在main.ts中的bootstrap中写好的:
js1async function bootstrap() { 2 const app = await NestFactory.create(AppModule) 3 await app.listen(3000) 4} 5bootstrap()
现在我们在浏览器访问http://localhost:3000/user/1,这个GET请求会经过@Controller('user')和@Get(':id')装饰器下的urlParam方法,将结果返回给我们:
js1received: id = 1
通过 query
通过 query 传输的形式是这样的:
js1http://localhost/user?id=xxx&age=30
如果用 query 传输,可以使用 query-string或者 qs将对象解析成xx=xx&yy=yy这样的字符串编码,同时这些三方库会对非英文字符做编码处理。
如果自己写的话,则需要手动调用 API 对一些非英文字符做编码。
对整个 URL 做编码使用
encodeURI,单独对部分做编码则使用encodeURIComponent。
js1const query = '?name=' + encodeURIComponent('加密') 2// '?name=%E5%8A%A0%E5%AF%86'
跟 url param 类似,我们继续用装饰器定义一个新的方法:
ts1 @Get('find') 2 urlQuery(@Query('id') id: string, @Query('name') name: string) { 3 return `received from Query: id=${id} name=${name}`; 4 }
不同之处在于没有使用:声明,以及提取参数用的装饰器为@Query('参数名')。
继续在浏览器访问:
js1http://localhost:3000/user/find?id=123&name=qiuyanxi
可以得到结果:
js1received from Query: id=123 name=qiuyanxi
注意现在的代码:
ts1@Controller('user') 2export class UserController { 3 // query在前 4 @Get('find') 5 urlQuery(@Query('id') id: string, @Query('name') name: string) { 6 return `received from Query: id=${id} name=${name}` 7 } 8 // param在后 9 @Get(':id') 10 urlParam(@Param('id') id: string) { 11 return `received: id=${id}` 12 } 13}
这是因为Controller内的方法会按照顺序匹配,当访问user/find时,也可能会被user/:id路由匹配到。
通过 form-urlencoded
直接使用 form 表单提交时是采用form-urlencoded进行传输。
content-type是application/x-www-form-urlencoded。
它的意思是:“这是已编码为 URL 参数的表单数据。”
这是一种使用 Post 请求并将 url query放在 body 传输的方法。所以非英文的参数都需要经过编码(encode)。(方法同 query)
http1POST /test HTTP/1.1 2Host: foo.example 3Content-Type: application/x-www-form-urlencoded 4Content-Length: 27 5 6name=%E5%8A%A0%E5%AF%86&field2=value2
在 Nest 中接受 body 内的数据,需要用到 @Body 装饰器,Nest 会解析请求体,注入到 dto(data transfer object)中。
先定义一个 dto 对象
ts1// src/dto/person.dto.ts 2 3export class PersonDto { 4 name: string 5 age: number 6}
再使用@Post装饰器定义路由以及使用@Body装饰器取出 Body 内的参数。
ts1import { PersonDto } from 'src/dto/person.dto' 2 3@Controller('user') 4export class UserController { 5 @Post('get') 6 body(@Body() getPersonDto: PersonDto) { 7 return `received: ${JSON.stringify(getPersonDto)}` 8 } 9}
使用 postman 请求测试一下

通过 JSON
在 json 数据格式被广泛用于 HTTP 传输后,大多数的 REST API 都使用 json 传输数据。
content-type为application/json。
都是从 Body 中取值,Nest 内部做了封装,能够对应不同的 content-type 从 body 中拿到数据。所以代码几乎没有变化。
ts1 @Post('get') 2 getUserByJSON(@Body() getPersonDto: PersonDto) { 3 return `received: ${JSON.stringify(getPersonDto)}`; 4 }
唯一变化的就是前端请求时,需要修改content-type

通过 formdata
如果你想要发送文件,你需要使用 formdata 的方式传输。
content-type 为multipart/form-data。
因为数据将被分成多个部分,每个文件单独占用一个部分,表单正文中包含的文本数据(如果文本也输入到表单中)占用一个部分。
Nest 解析 form data 使用 FilesInterceptor 的拦截器,用 @UseInterceptors 装饰器启用,然后通过 @UploadedFiles 来取。
非文件的内容,同样是通过 @Body 来取。
接受文件的 Nest 代码如下:
ts1 @Post('form-data') 2 @UseInterceptors( 3 AnyFilesInterceptor({ 4 dest: 'uploads/', 5 }), 6 ) 7 body2( 8 @Body() getPersonDto: PersonDto, 9 @UploadedFiles() files: Array<Express.Multer.File>, 10 ) { 11 console.log('——————🚀🚀🚀🚀🚀 —— files:', files); 12 return `received: ${JSON.stringify(getPersonDto)}`; 13 }
上面的代码还需要额外安装Express.Multer的类型声明
bash1pnpm install -D @types/multer
接着用 postman 测试一下:

现在文件已经成功传输到dest: 'uploads/'中:

总结
Nest 对每种不同的 REST API 都做了封装,并且有不同的 Typescript 装饰器方便我们获取数据。
其中使用 URL 传输的为:
-
url param,即
http://localhost/user/:id,使用@Param 装饰器取数据 -
url query ,即
http://localhost/user?id=xxx,使用@Query 装饰器取数据
使用 body 传输的为:
-
form-urlencoded,将 url query 字符串 用 post 方式传输,使用@Body()装饰器取数据
-
json,json 数据传输,使用@Body()装饰器取数据
-
form-data ,适用于文件与文本数据传输,使用@UploadedFiles 装饰器取数据
此外,还有一些装饰器:
- @Get 装饰器声明 get 请求。
- @Post 装饰器声明 post 请求。
- @UseInterceptors 装饰器启用拦截器。
- @AnyFilesInterceptor 装饰器对文件进行拦截并且解析。