控制器
控制器负责处理传入的 HTTP
请求和响应客户端的工作,主要就是处理用户发出的请求和响应。
一个具有 @Controller
装饰器的类,表示这个类是个控制器类。需要在 module
中导入到对应的元数据参数 controllers
列表中,这样 nest
的 IoC容器
才会安装该控制器的实例,才能被你的 Nest
应用所使用。
定义控制器:
// 被 @Controller 装饰的类,是一个控制器类
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello()
}
}
注册控制器,添加在模块的 controllers
数组中,这样 Nest
就可以反射出哪些类是需要被容器管理的:
@Module({
// 注册控制器
controllers: [AppController]
})
export class AppModule {}
接下来,我们就可以使用控制器处理 HTTP
请求和响应了。
- 使用不同请求方法
@Get @Post @Put @Delete
等分发处理不同类型的请求 - 使用
@Body()、@Query()、@Param()、@Request/@Req、@Response/@Res、@Header()
等属性装饰器处理来自HTTP
请求的参数、请求头 - 使用
@HttpCode()控制响应状态码、@Redirect()路由重定向
等方法装饰器完成特定需求
请求方法
在 Nest
中一共有 Get、Post、Delete、Put、Patch、Options
等请求方法,之前提及过符合 RESTFul
常用的请求方法—— Get、Post、Delete、Put
。
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {}
@Post()
create() {}
@Put()
update() {}
@Delete()
delete() {
return '删除用户成功'
}
}
路由通配符
控制器装饰器传递的参数,作为控制器的路径定义,比如 user
,路径支持路由通配符:
- 动态路由参数:使用冒号
:
可以创建动态路由参数。例如,/users/:id
可以匹配/users/123
,并将 123 作为参数传递给相应组件。 - 可选动态路由参数:使用括号
()
可以创建可选的动态路由参数。例如,/users/:id(\\d+)?
可以匹配/users/123
或/users/
。在这个例子中,:id
是可选的,且限定为数字。` - 通配符
*
:用于匹配零个或多个字符(不包括斜杠/
)。例如,/users/*
可以匹配/users/123
、/users/admin
等路径。通常用于捕获多级路径。 - 通配符
**
:用于匹配任意字符,包括斜杠/
。通常需要配合正则表达式使用。例如,/static/**
可以匹配/static/js/app.js
、/static/css/style.css
等路径。通常用于匹配文件资源等。 - 正则表达式:使用正则表达式可以定义更复杂的匹配规则。例如,
/users/\\d+
可以匹配/users/123
,其中\\d+
表示匹配一个或多个数字字符。
参数
在 数据传输 的章节,已经了解过请求参数大致分为两类:
URI
路径参数body
请求体参数
之前由 Express
完成的案例,在本节中会由 Nest
完全重制。
TIP
这些方法定义在控制器内,分发处理来自客户端的请求,由于 nest
是由 expressjs和fastjs
封装,所以 express
的 request、response
在 nest
中也是能够使用的,并且使用方法没有发生太大的改变。
新建一个 User
的模块,完成案例。
query
对于参数的获取,直接使用 Query
装饰器,直接解析获取 query
参数,无需像 Express
通过 req.query
去获取。
Query
装饰器还可以传递参数名,直接解析提取参数。
import { Controller, Get, Query } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
@Get()
findAll(@Query('name') query) {
return {
code: 200,
message: query.name,
};
}
// ...
}
Request/Response
Request、Response
装饰器,能让你像在 Express
中使用 req、res
对象一样在 Nest
中使用它们。
在任何请求方法中,都能使用 express.requestObject
,如果你想像在 Express
中操作 requestObject
对象的话。
import { Controller, Get, Query, Req, Res } from '@nestjs/common';
import { UserService } from './user.service';
import { Request, Response } from 'express';
@Controller('user')
export class UserController {
@Get('req')
requestObject(@Req() req: Request, @Res() res: Response) {
res.send({
code: 200,
message: req.query.name,
});
}
// ...
}
param
TIP
当动态参数和子路由同时存在时,Nest
在处理请求时会按照控制器中路由的定义顺序来匹配路由。当动态路由定义在静态子路由前面时,就会出现冲突。因此,确保动态路由定义在最后方是解决冲突的策略之一。
import { Controller, Get, Param } from '@nestjs/common';
@Controller('user')
export class UserController {
@Get(':id')
findOne(@Param('id') id) {
const mockUsers = [
{
id: 1,
name: '张三',
},
{
id: 2,
name: '李四',
},
];
return {
code: 200,
data: mockUsers.find((user) => user.id === +id),
};
}
// ...
}
body: form-urlencoded
键和值编码为键值元组,以 '&'
分隔,需要指定此次请求的类型为:application/x-www-form-urlencoded
import { Body, Controller,Post } from '@nestjs/common';
@Controller('user')
export class UserController {
@Post('urlencoded')
urlencodedCreate(@Body() body) {
return {
code: 200,
data: body,
};
}
// ...
}
body: json
指定请求头的类型为: application/json
,前端的请求库 axios
默认的类型就是 json
。
import { Body, Controller,Post } from '@nestjs/common';
@Controller('user')
export class UserController {
@Post('json')
jsonCreate(@Body() body) {
return {
code: 200,
data: body,
};
}
// ...
}
body: form-data
这个类型的参数多数用来传输文件,Nest
解析 form-data
的文件,需要开启一个拦截器
import {
Body,
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor, FileInterceptor } from '@nestjs/platform-express';
@Controller('user')
export class UserController {
@Post()
@UseInterceptors(
AnyFilesInterceptor({
dest: 'uplodas/',
}),
)
formDataCreate() {}
// ...
}
然后再通过 @UploadedFiles
参数装饰器去取数据,非文件的其他参数,依然使用 @Body
去获取。
import {
Body,
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor, FileInterceptor } from '@nestjs/platform-express';
@Controller('user')
export class UserController {
@Post('form-data')
@UseInterceptors(
AnyFilesInterceptor({
dest: 'uplodas/',
}),
)
formDataCreate(
@Body() body,
@UploadedFiles() files: Array<Express.Multer.File>,
) {
return {
code: 200,
data: {
filename: files[0].filename,
name: body.name,
},
};
}
}
客户端发送 Post
请求,指定 Content-Type
为 multipart/form-data
:
请求头
使用 @Headers
装饰器获取 HTTP
请求的请求头:
import { Controller, Get, Headers } from '@nestjs/common';
@Controller('user')
export class UserController {
@Get('/get/headers')
getHeaders(@Headers() headers) {
return {
contentType: headers['content-type'],
};
}
// ...
}
状态码
返回指定的 HTTP
状态码:
import { Controller, Get, HttpCode } from '@nestjs/common';
@Controller('user')
export class UserController {
@Get('/get/status-code')
@HttpCode(204)
getStatusCode() {}
// ...
}
通常情况下,你的状态码并不是静态的,而是取决于各种因素。在这种情况下,你可以使用特定于库的响应(使用 @Res()
注入)对象(或者在出现错误时抛出异常)。
重定向
重定向到指定的 URL
路径,实际上 Nest
在底层会直接调用 requestObject.redirect()
@Redirect()
接受两个参数,并且 statusCode
, url
都是可选的。如果省略,则默认值 statusCode
为 302
。
import { Controller, Get, Redirect } from '@nestjs/common';
@Controller('user')
export class UserController {
@Get('/get/redirect')
@Redirect('https://nestjs.com', 301)
redirect() {}
// ...
}
嵌套路由
当程序体量达到一定规模之后,一个控制器可能会包含十几个控制器,控制器映射的路由路径并不是根据模块或引用关系决定的,而是在 @Controller()
中定义的。
比如 user
模块,如果它下面还有 info
子模块,就需要在子模块定义路径:@Controller('user/info')
// user模块控制器
@Controller('user')
export class UserController {}
// user/info 嵌套路径模块控制器
@Controller('user/info')
export class InfoController {
@Get()
getNested(@Req() req) {
return {
code: 200,
message: '嵌套路径',
url: req.url
}
}
}
总结
控制器用于处理传入的 HTTP
请求,并根据请求返回响应。主要任务包括:
- 处理请求:接受请求,解析请求参数,并执行相应的操作。
- 返回响应:根据请求的结果,构建并返回
HTTP
响应。
在处理请求时,其表现与在 Express
中一致,你可以正常返回响应,也可以调用 RequestObject
返回响应。
控制器方法会返回不同类型的值,这些值将会作为 HTTP
响应返回给客户端
- JSON:返回json对象,
Nest
会自动序列化返回给客户端。 - Promise:
Nest
会等待Promise
完成,并将其结果返回给客户端。 - Observables:如果你使用
RxJs
,你也可以返回一个Observable
,Nest
会订阅Observable
并发送事件流给客户端。