五、控制器 Controllers
控制器:负责处理客户端的请求数据,并将结果返还。它的目的就是接收客户端的各种任务,这个机制则是用了类似前端路由的一种控制。路由是将所需要的请求放在不同的 URL 中,根据不同的需求,访问不同的 URL。
Nest 的控制器一般都使用装饰器进行快速的配置、接收请求。Nest 提供了一个快速创建 CRUD 控制器的生产方法:nest g resource [组件名称]
。
Nest 提供了很多的装饰器帮助开发者快速获取请求参数等,其中常用的有:
装饰器 | 介绍 |
---|---|
@Controller(route?:string) | 控制器类上必加:让程序识别控制器 |
@Request() / @Req() | 接收请求参数 |
@Response() / @Res() | 接收返回参数 |
@Param(key?:string) | 接收请求参数中的param(需要在路由中标记) |
@Body(key?:string) | 接收请求参数中的body(post) |
@Query(key?:string) | 接收请求参数中的query(get) |
@Headers(name?:string) | 接收请求头 |
@HttpCode() | 设置返回状态码 |
@Session() | 设置session(需要安装express-session) |
@Redirect(url:string, statusCode:number) | 重定向:需设置地址和状态码(3xx) |
Nest 采用的请求风格是RESTful API 接口的设计风格。REST 是一种体系结构样式,一般用于web服务开发。REST 的出现是为了解决,当需要请求复杂的数据时,需要个固定的模式,这样对于开发者来说,代码请求会更加的有组织、简洁。
在 URL 设置方面,REST 的请求参数则是通过 ‘/’,而非像传统形式的 ‘?’:
- 传统URL带参数:http://localhost:8080/api/get?id=1
- REST风格URL:http://localhost:8080/api/1
对于增删改查的操作,REST 则是通过不同的请求方式来区分,这样即便时同一个接口也可以实现不同的操作。它遵循的是CRUD操作,各对应了:
操作 | Http 方法 |
---|---|
C = Create(增) | POST |
R = Read(查) | GET |
U = Update(改) | PUT |
D = Delete (删) | DELETE |
对比传统设计风格可以明显看出有所不同。传统方式会以不同URL进行不同的操作。而遵循 REST 风格的web应用(RESTful)的 URL 是不变的,只是根据不同的Http请求方法,提供了不同的服务,并且用 Http 协议中的 status code 定义操作结果。
操作 | 传统设计 | REST架构 |
---|---|---|
查询 | /user/getUser | GET /user |
新增 | /user/addUser | POST /user |
更新 | /user/updateUser | PUT /user |
删除 | /user/deleteUser | DELETE /user |
当然,这只是一套规范,写项目时只是建议这样写,但不是必须遵循的。
Get请求
Get 请求必须在方法之上加入 @Get() 装饰器,路由参数可以选择填入或否。Get 请求参数可以通过 @Req() 或者 @Query() 装饰器获取,相同的方式,@Headers() 也可以获取请求头部信息。也可以选择直接选取并获得参数中的某个元素,只需要在装饰器中传入对应的元素名称即可。
import { Controller, Get, Req, Query, Headers } from '@nestjs/common';
import { DemoService } from './demo.service';
@Controller('demo')
export class DemoController {
constructor(private readonly demoService: DemoService) { }
@Get()
getData(@Req() req, @Query() query, @Headers('cookie') cookie) {
console.log(req.query === query) // true
console.log(cookie) // 从请求头部中直接获取cookie
return { code: 200 }
}
}
Post请求
Post 请求参数同样可以通过 @Req() 来获取,或者直接接收 @Body() 参数,亦或者直接传入参数名称。
import { Controller, Post, Body, Req } from '@nestjs/common';
import { DemoService } from './demo.service';
@Controller('demo')
export class DemoController {
constructor(private readonly demoService: DemoService) { }
@Post()
create (@Req() req, @Body('name') body) {
console.log(req.body, body.name) // 可获取请求体中的name元素
return { code:200 }
}
}
动态路由
Nest 应用中可以接收动态参数,通过此参数访问不同的网页。接收方式同样可以使用 @Req(),又或者用 @Param() 直接接收。
import { Controller, Get, Param, Req } from '@nestjs/common';
import { DemoService } from './demo.service';
@Controller('demo')
export class DemoController {
constructor(private readonly demoService: DemoService) { }
@Post(':id') //路由为:localhost:port/demo/1
create (@Req() req, @Param() param) {
console.log(req.param) // { id: 1 }
console.log(param.id) // 1
return { code:200 }
}
}
状态码
通过 @HttpCode() 可以直接返还对应的状态码。在 REST 接口中,通常也有状态码的代码规范:
- 2xx开头:成功
- 200 OK(操作成功,正常返回)
- 201 Created(操作成功,已经在处理该请求)
- 202 Accepted(异步处理)
- 3xx开头:重定向(参数方面异常)
- 300 Multiple Choices(参数类型错误)
- 301 Moved Permanently(资源地址已更新)
- 302 Found(参数超出正常取值范围)
- 303 See Other(token过期)
- 304 Not Modified(token无效)
- 4xx开头:客户端异常(请求地址异常)
- 400 Bad Request(找不到地址)
- 401 Unauthorized(token错误)
- 403 Forbidden referer origin(验证失败)
- 404 Not Found(接口不存在)
- 406 Not Acceptable(服务端不支持所需表示)
- 409 Conflict(通用冲突)
- 5xx开头:服务端异常
- 500 Internal Server Error(服务端错误)
- 501 Not Implemented(不存在)
- 502 Bad Gateway(上有接口或者服务器有问题)
- 503 Service Unavailable(服务端当前无法处理请求)
在 Nest 项目中,通过装饰器或者直接返回状态码结果都是可以的。
import { Controller, Post, HttpCode } from '@nestjs/common';
import { DemoService } from './demo.service';
@Controller('demo')
export class DemoController {
constructor(private readonly demoService: DemoService) { }
@Post()
@HttpCode(500)
create () {
return { }
}
}
REST版本控制
REST API 定义版本控制是为了解决一些版本不兼容的问题。可以使用不同的 URL 对应不同的版本,这样可以做到同时兼容多个版本。
RESTful 中拥有三种不同的版本控制类型,一般使用默认的 URI 资源定位符。
- URI Versioning:版本将在请求的 URI 中传递(默认)
- Header Versioning:自定义请求标头将指定版本
- Media Type Versioning:请求的Accept标头将指定版本
Nest 中需要先在入口文件导入:
import { NestFactory } from '@nestjs/core';
import { VersioningType } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({ type: VersioningType.URI })
await app.listen(3000);
}
bootstrap();
接下来就可以直接在控制器中使用不同的版本。第一种方式是直接将整个控制器放在同一个版本,在 @Controller 装饰器上设置。另一种则是通过 @Version 装饰器设置某个特定的接口的版本。
import { Controller, Get, Version } from '@nestjs/common';
import { DemoService } from './demo.service';
@Controller({
path: 'demo', // 路由地址可以通过path传递
version:'1' // 1. 设置整个控制器版本
})
export class DemoController {
constructor(private readonly demoService: DemoService) {}
@Get()
// @Version('1') // 2. 指定接口本版
getVersion() { return 'Version 1' }