来聊一聊最基本的 Nest.JS 控制器吧
如上图所示的这样,控制器是用来处理客户端传进来的HTTP请求之后再向客户端返回响应数据的。
控制器通过HTTP request接受应用的特殊请求,但是有很多个控制器,因而需要路由机制来控制这个请求是哪个控制器接受的。一般控制器里有多个路由,每一个路由有自己要干的事情
控制器基本创建需要两样东西:类+装饰器
,其中装饰器可以把类、元数据相关联,创建路由映射让请求绑定到控制器
路由
创建路由的关键字是@Controller
,路由路径前缀随意设置,比如说wee
import { Controller, Get } from '@nestjs/common';
@Controller('wee')
export class WeeController {
@Get()
findall(): string {
return 'Hello Wibus!'
}
}
使用 CLI 创建控制器:
nest g controller controller-name
通过访问 http://localhost:3000/wee/ 可以看到返回了Hello Wibus!而这串代码到底是干嘛的?
@Get()
可以为请求的特定端点创建处理程序,端点对应于 HTTP
请求方法(在本例中为 GET )和路由路径(如 GET /wee
)。如果我改成@Get('hello')
的话,那你需要访问 http://localhost:3000/wee/hello 来看到这句话了,@Controller('wee')
可以认为是路径前缀的设置,而@Get
就是装饰器
什么是路由路径 ? 一个处理程序的路由路径是通过连接为控制器 (Controller) 声明的(可选)前缀和请求装饰器中指定的任何路径来确定的。
当我们对这个端点发出HTTP请求的时候,Nest会把这个请求路由到我们的findall()
方法,但是要说明的是findall是一个自定义方法。而我们必须声明一个绑定到路由上的函数,虽然Nest不会赋予特别意义(甚至是任何意义都不赋予
这个函数可以返回200状态码和其他的响应,但是从我们看来似乎只有一个string字符串,因为Next将会使用两种不同操作响应选项
- 标准(推荐)
使用这个内置方法,当请求处理程序返回一个 JavaScript
对象或数组时,它将自动序列化为 JSON
。但是,当它返回一个 JavaScript
基本类型(例如string、number、boolean
)时, Nest 将只发送值,而不尝试序列化它。这使响应处理变得简单:只需要返回值,其余的由 Nest 负责。
- 类库特有的
我们可以在函数签名处通过 @Res()
注入类库特定的响应对象(例如, Express
)。使用此方法,你就能使用由该响应对象暴露的原生响应处理函数。例如,使用 Express
,可以使用 response.status(200).send()
构建响应
Request
有时我们需要访问客户端的更多请求细节,根据上面的路由代码,加上Express。我们可以在处理函数当中使用@Req()
装饰器,它会让nest把请求对象注入处理程序
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('wee')
export class WeeController {
@Get('hello')
findall(@Req() request: Request): string {
return 'Hello Wibus!'
}
}
对比与之前的代码,新增了@Req() request: Request
这一句话,Request代表HTTP请求,详细的在这里 https://expressjs.com/en/api.html#req
在多数情况下,不必手动获取它们。 我们可以使用专用的装饰器,比如开箱即用的
@Body()
或@Query()
Decorator | 对应的 Code |
---|---|
@Request(),@Req() | req |
@Response(),@Res()* | res |
@Next() | next |
@Session() | req.session |
@Param(key?: string) | req.params /req.params[key] |
@Body(key?: string) | req.body /req.body[key] |
@Query(key?: string) | req.query /req.query[key] |
@Headers(name?: string) | req.headers /req.headers[name] |
@Ip() | req.ip |
@HostParam() | req.hosts |
资源
在上面我们通过@Get()
创建了一个端点来获取wee的数据。但是一般来讲我们还要多一个创建新数据的端点,这个时候就需要用@Post()
了
import { Controller, Get, Post } from '@nestjs/common';
@Controller('wee')
export class WeeController {
// POST路由
@Post()
create(): string{
return 'This action will add a new data'
}
// Get路由
@Get('hello')
findall(): string {
return 'Hello Wibus!'
}
}
就这么简单。 Nest 为所有标准的 HTTP 方法提供了相应的装饰器:@Put()
、@Delete()
、@Patch()
、@Options()
、以及 @Head()
。
此外,@All()
则用于定义一个用于处理所有 HTTP 请求方法的处理程序。
export class WeeController {
@All()
findall(): string {
return 'Hello Wibus!'
}
}
路由通配符
路由同样支持模式匹配。例如,星号被用作通配符,将匹配任何字符组合。
@Get('h*llo')
findall(): string {
return 'Hello Wibus!'
}
这样的话,他将会匹配abcd
、ab_cd
、abecd
等。字符 ?
、+
、 *
以及 ()
是它们的正则表达式对应项的子集。连字符(-
) 和点(.
)按字符串路径逐字解析。
状态码
一般来讲,请求都会返回200,当然我们可以添加一个装饰器来改变响应状态码
@Get('h*llo')
@HttpCode(404)
findall(): string {
return 'Hello Wibus!'
}
Headers
如果要指定响应头,使用@header()
装饰器来修改
@Get('h*llo')
@Header('Cache-Control', 'none')
findall(): string {
return 'Hello Wibus!'
}
重定向
@Redirect()
装饰器有两个可选参数,url
和 statusCode
。 如果省略,则 statusCode
默认为 302
。
@Get()
@Redirect('https://nestjs.com', 302)
路由参数
如果需要接受dynamic data作为请求的一部分时(如 http://localhost:3000/wee/1 )带有静态路径的路由将无法工作,也就是说我们目前所讲的用法并没有这种用法,这个时候我们需要在路由路径当中添加路由参数表计,就像这样
@Controller('wee')
export class WeeController {
@Get('h*llo/:id')
findall(@Param() params): string {
return 'Hello Wibus! ID: ' + params.id
}
}
@Param()
用于修饰一个方法的参数(上面示例中的 params
),并在该方法内将路由参数作为被修饰的方法参数的属性。如上面的代码所示,我们可以通过引用 params.id
来访问(路由路径中的) id
参数。 您还可以将特定的参数标记传递给装饰器,然后在方法主体中按参数名称直接引用路由参数。
@Get(':id')
findOne(@Param('id') id): string {
return `This action returns a #${id} cat`;
}
请求负载
在前文中的@Post()
并没有接受任何参数,这个时候需要用@Body()
来解决这个问题
首先(如果您使用 TypeScript),我们需要确定
DTO
(数据传输对象)模式。DTO
是一个对象,它定义了如何通过网络发送数据。我们可以通过使用 TypeScript 接口(Interface)或简单的类(Class)来定义 DTO 模式。有趣的是,我们在这里推荐使用类。类是 JavaScript ES6 标准的一部分,因此它们在编译后的 JavaScript 中被保留为实际实体。另一方面,由于 TypeScript 接口在转换过程中被删除,所以 Nest 不能在运行时引用它们。这一点很重要,因为诸如管道(Pipe)之类的特性为在运行时访问变量的元类型提供更多的可能性。
export class CreateCatDto {
readonly name: string;
readonly age: number;
}
export class WeeController {
@Post()
@HttpCode(200)
create(@Body() createCatDto: CreateCatDto): string{
return `name:${createCatDto.name}, age:${createCatDto.age}`;
}
}