Nest 控制器
Nest 控制器
控制器 (Controllers) 负责处理传入的请求和向客户端返回响应。
创建控制器
使用如下命令可以快速创建一个控制器。
nest generate controller <CONTROLLERS_NAME>
<CONTROLLERS_NAME>可以是文件路径,例如user/auth。
这里我们创建一个 todo 控制器。
nest generate controller todo此时会在 src/todo 下生成一个 todo.controller.ts 文件和一个 todo.controller.spec.ts 文件。
todo.controller.ts 文件的内容如下。
import { Controller } from '@nestjs/common'
@Controller('todo')
export class TodoController {}同时,在 app.module.ts 文件中自动引入了 TodoController。
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { TodoController } from './todo/todo.controller'
@Module({
imports: [],
controllers: [AppController, TodoController],
providers: [AppService]
})
export class AppModule {}路由
在 todo.controller.ts 文件中,可以看到如下代码。
import { Controller } from '@nestjs/common'
@Controller('todo')
export class TodoController {}这里 @Controller() 里面的参数 todo 就是路由前缀,所有该控制器下的路由都会加上这个前缀。
注意:通过 Nest CLI 建立的控制器路由前缀默认使用该控制器的名称,而我们通常会习惯把名称取单数,而前缀改为复数。
所以,我们修改一下路由前缀。
import { Controller } from '@nestjs/common'
@Controller('todos')
export class TodoController {}Http Method 装饰器
通过在 class 方法上添加 Http Method 装饰器,可以定义路由的 HTTP 方法,Nest 会根据装饰器自动将请求映射到对应的方法上。
import { Controller, Get } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get()
getAll() {
return []
}
}这里我们定义了一个 GET /todos 的路由,并且返回一个空数组。通过 http://localhost:3000/todos 可以访问到这个路由。
Nest 为所有标准 HTTP 方法提供了装饰器:
@Get():GET 请求。@Post():POST 请求。@Put():PUT 请求。@Delete():DELETE 请求。@Patch():PATCH 请求。@Options():OPTIONS 请求。@Head():HEAD 请求。@All():所有请求。
子路由
如果我们想定义一个 GET /todos/examples 的路由,我们不可能每次有新子路由时都新增一个控制器,这时我们可以在 Http Method 装饰器中添加路径参数。
import { Controller, Get } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get('/examples')
getExample() {
return [
{
id: 1,
title: 'Example 1',
description: 'This is an example todo'
}
]
}
}通过 http://localhost:3000/todos/examples 可以访问到这个路由。
路由通配符
在设计路由时,可能需要一些容错,比如我们的 GET /todos/examples 路由,如果用户不管是输入了 GET /todos/exammmmmples 还是 GET /todos/exam_ples,我们都可以返回 GET /todos/examples 的结果,这时我们就可以使用路由通配符 *。
import { Controller, Get } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get('/exam*ples')
getExample() {
return [
{
id: 1,
title: 'Example 1',
description: 'This is an example todo'
}
]
}
}通过 http://localhost:3000/todos/exammmmmples 和 http://localhost:3000/todos/exam_ples 都可以访问到这个路由。
获取请求参数
路由参数
要获得路由参数,我们先在 Http Method 装饰器上进行定义,字符串格式为 :参数名,然后在该方法中添加带有 @Param() 装饰器的参数。
import { Controller, Get, Param } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get(':id')
getById(@Param() params) {
const { id } = params
return {
id,
title: `Title ${id}`,
description: `Description ${id}`
}
}
}通过 http://localhost:3000/todos/1 可以访问到这个路由,将返回 {"id":1,"title":"Title 1","description":"Description 1"}。
也可以通过 @Param('参数名') 来获取参数。
import { Controller, Get, Param } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get(':id')
getById(@Param('id') id) {
return {
id,
title: `Title ${id}`,
description: `Description ${id}`
}
}
}通过 http://localhost:3000/todos/1 可以访问到这个路由,将返回 {"id":1,"title":"Title 1","description":"Description 1"}。
查询参数
要获得查询参数,我们只需要在方法中添加带有 @Query() 装饰器的参数即可。
import { Controller, Get, Query } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get()
getList(@Query() query) {
return {
query
}
}
}通过 http://localhost:3000/todos?id=1&name=2 可以访问到这个路由,将返回 {"query":{"id":"1","name":"2"}}。
也可以通过 @Query('参数名') 来获取指定参数。
import { Controller, Get, Query } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get()
getList(@Query('id') id, @Query('name') name) {
return {
id,
name
}
}
}通过 http://localhost:3000/todos?id=1&name=2 可以访问到这个路由,将返回 {"id":"1","name":"2"}。
请求体
要获得请求体,我们只需要在方法中添加带有 @Body() 装饰器的参数即可。
import { Body, Controller, Post } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Post()
create(@Body() data) {
const id = 1
return {
id,
...data
}
}
}通过发送 POST 请求到 http://localhost:3000/todos,发送请求体 { "title": "Title 1", "description": "Description 1" },可以访问到这个路由,将返回 {"id":1,"title":"Title 1","description":"Description 1"}。
也可以通过 @Body('参数名') 来获取指定参数。
import { Body, Controller, Post } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Post()
create(@Body('title') title, @Body('description') description) {
const id = 1
return {
id,
title,
description
}
}
}通过发送 POST 请求到 http://localhost:3000/todos,发送请求体 { "title": "Title 1", "description": "Description 1" },可以访问到这个路由,将返回 {"id":1,"title":"Title 1","description":"Description 1"}。
使用 DTO
DTO (数据传输对象) 通常用于过滤、格式化数据,它只负责传输数据,不包含业务逻辑。
我们可以使用 TypeScript 的接口来定义 DTO,也可以使用 JavaScript 的类来定义 DTO,由于 TypeScript 的接口在编译后会被删除,所以使用类定义 DTO 是更好的选择。
我们在控制器的目录下创建一个 dto 文件夹,并在其中创建一个 create-<CONTROLLER_NAME>.dto.ts 文件。这里我们创建一个 create-todo.dto.ts 文件。
export class CreateTodoDto {
title: string
description?: string
}创建完成后在控制器中使用,将带有 @Body() 装饰器的参数类型指定为 DTO 类型。
import { Body, Controller, Post } from '@nestjs/common'
import { CreateTodoDto } from './dto/create-todo.dto'
@Controller('todos')
export class TodoController {
@Post()
create(@Body() dto: CreateTodoDto) {
const id = 1
return {
id,
...dto
}
}
}请求头
要获取请求头,我们只需要在方法中添加带有 @Headers() 装饰器的参数即可。
import { Controller, Headers, Post } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Post()
head(@Headers() header) {
return header.name
}
}通过发送 POST 请求到 http://localhost:3000/todos,并设置请求头 name: 'John',可以访问到这个路由,将返回 John。
也可以通过 @Headers('参数名') 来获取指定参数。
import { Controller, Headers, Post } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Post()
head(@Headers('name') name) {
return name
}
}通过发送 POST 请求到 http://localhost:3000/todos,并设置请求头 name: 'John',可以访问到这个路由,将返回 John。
参数装饰器
Nest 提供了多种参数装饰器,用于从参数中获取不同的信息。
@Request():获取请求对象,简写为@Req()。@Response():获取响应对象,简写为@Res()。@Next():获取下一个中间件的引用。@Session():获取会话对象。@Param():获取路由参数。@Query():获取查询参数。@Body():获取请求体。@Headers():获取请求头。@Ip():获取客户端 IP 地址。@HostParam():获取主机参数。
响应头
使用 @Header() 装饰器可以设置响应头。
import { Controller, Get, Header } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get()
@Header('name', 'todo')
getTodos() {
return {
message: 'This is the todo list'
}
}
}通过发送 GET 请求到 http://localhost:3000/todos,可以访问到这个路由,将返回 {"message":"This is the todo list"},并且响应头中会包含 name: todo。
状态码
默认情况下,响应的状态码始终为 200,除了 POST 请求的状态码为 201。Nest 提供了 @HttpCode() 装饰器来设置状态码,同时还提供了状态码的 enum。
import { Controller, HttpCode, HttpStatus, Patch } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Patch()
@HttpCode(HttpStatus.NO_CONTENT)
get() {
return []
}
}通过发送 PATCH 请求到 http://localhost:3000/todos,可以访问到这个路由,状态码将返回 204 No Content。
处理响应的方式
Nest 提供了两种方式来处理响应。
标准方式
通过 return 关键字返回。
标准方式支持异步。
import { Controller, Get } from '@nestjs/common'
@Controller('todos')
export class TodoController {
@Get()
async getTodos() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
todos: 'this is the todos'
})
}, 10000)
})
}
}标准方式也支持返回 RxJS observable 流。
import { Controller, Get } from '@nestjs/common'
import { Observable } from 'rxjs'
import { of } from 'rxjs'
@Controller('todos')
export class TodoController {
@Get()
getTodos(): Observable<any> {
return of({
todos: 'this is the todos'
})
}
}特定于库的方式
这个方式不通过 return 关键字返回,而是通过使用 @Res() 装饰器注入来获取响应对象,然后调用响应对象的方法来处理响应。
import { Controller, Get, Res } from '@nestjs/common'
import { Response } from 'express'
@Controller('todos')
export class TodoController {
@Get()
getTodos(@Res() res: Response) {
res.status(200).json({
todos: 'this is the todos'
})
}
}限制
Nest 会检测处理程序是否使用了 @Res()、@Response() 或 @Next() 装饰器,如果是,则会启用特定于库的处理方式,而标准方式则会被禁用。也就是说,return 的方式将不再起作用。
import { Controller, Get, Res } from '@nestjs/common'
import { Response } from 'express'
@Controller('todos')
export class TodoController {
@Get()
getAll(@Res() res: Response) {
return []
}
}可以通过在装饰器中添加 { passthrough: true } 选项来突破此限制。
import { Controller, Get, Res } from '@nestjs/common'
import { Response } from 'express'
@Controller('todos')
export class TodoController {
@Get()
getAll(@Res({ passthrough: true }) res: Response) {
return []
}
}