Nest
(NestJS
) 是一个用于搭建高性能、可伸缩的Node.js
服务端应用的框架。它使用渐进式的JavaScript
,内置且彻底支持Typescript
(但开发人员能够使用纯JavaScript
进行编程) , 同时结合了OOP
(面向对象编程),FP
(函数式编程)和FRP
(响应式编程)的原理。html
在传统Node.js
服务端应用中,controller
用于处理route
对应的客户端请求。每一个controller
能够拥有多个route
,不一样的route
会执行不一样的action
(行为)。在Nest
中,咱们能够使用内置的decorators
来对request
, response
, controller
、route
、action
等元素进行定制化的修饰。从而抽离重复代码,提升开发效率。以下所示:react
// 省略依赖代码
@Controller('cats') // 模块名称,用于定义route前缀:/cats
export class CatsController {
// 修改该route的成功响应时的状态码为204
@HttpCode(204)
// 修改该response的响应头参数
@Header('Cache-Control', 'none')
// 修饰该route([POST] /cats)的请求方法:POST,@Get、@Post、@Delete同理
@Post()
// @Bady 修饰参数,此处将会传入request.body,@Query()、@Param同理
create(@Body() createCatDto: CreateCatDto) {
// 返回响应的数据
return 'This action adds a new cat';
}
@Redirect("http://www.fzzf.top") // 路由重定向
@Get(":id") // [GET] /cats/:id
show(@Query("") query: ListAllEntities) {
// do something
}
}
复制代码
对于controller
的返回值,Nest
支持基于async/await
返回的promise
,也支持基于RxJS
返回的Observable Streams
,示例以下:es6
import { of } from 'rxjs';
// controller代码省略
@Put(":id")
async update(@Param() params: UpdateCatDto): Promise<any> {
return this.catsService.update(params);
}
// 注意不要加async
@Get()
index(@Query() query: IndexCatDto): Observable<any[]> {
return of(this.catsService.find(query));
}
复制代码
在Nest
中,provider
被定义为拥有特定功能方法集合的类。经过装饰器@Injectable()
修饰后成为能够做为依赖注入到Nest Module
的服务。处于相同的做用域的provider
之间只要,其中一个provider
也能够做为依赖注入到另外一个provider
中。以下代码所示:express
// cats.service
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
constructor(
private readonly otherService: OtherService
)
async create(values: any): Promise<any> {
return this.otherService.create(values);
}
}
复制代码
咱们能够声明一个module
(参考下一节),将provider
注入到controller
中。依赖注入的细节由Nest
框架内部完成,这里只介绍基本的依赖注入姿式,以下面代码所示:编程
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController], // 当前module的controller
providers: [CatsService], // 须要注入的provider
})
export class CatModule {}
// cats.controller
import { CatsService } from './cats/cats.service';
@Controller('cats')
export class CatsController {
constructor(
private readonly catsService: CatsService // 依赖注入
) { }
@Post()
create(@Body() createCatDto: CreateCatDto) {
// 返回响应的数据
return this.catsService.create(createCatDto);
}
}
复制代码
module
是Nest
应用的基础组成单位。一个Nest
应用至少拥有一个module(root module)
。简单来讲,在Nest
中,n个功能module
经过依赖注入组成一个业务module
,n个业务 module
组成一个Nest
应用程序。不管是controller
仍是provider
都须要经过module
注册才能完成依赖的注入。下面的代码介绍了基础的module
声明姿式。json
@Module({
// 依赖的其余module类
imports: [],
// 业务模块所需的controller类,它将以为module如何是实例化为业务模块仍是功能模块
controller: [],
// 提供依赖的类
provider: [],
// 导出模块中provider,能够被其余模块依赖注入
exports: [],
})
export class AppModule {}
复制代码
这里咱们能够经过传递exports
参数,才完成module
之间层级依赖,以及不一样module
中的provider
可以被复用。以下面代码所示:api
// request.module.ts
@Module({
// 注册provider
provider: [RequestService],
// 导出注册的provider
exports: [RequestService],
})
export class RequestModule {}
// api.module.ts
@Module({
// 导入一个RequestModule,Nest会注册其中export的provider
imports: [RequestModule],
// 注册controller
controllers: [ApiController],
})
export class ApiModule {}
// api.controller.ts
@Controller('api')
export class ApiController {
constructor(
private readonly requestService: RequestService // 依赖注入
) { }
}
复制代码
除此以外,咱们还能够经过装饰器@Global
将module A
修饰为一个全局模块,这样任意module
不须要import
就能够注入module A
中export
的依赖。同时咱们也能够经过特定参数实现module
的动态导入。promise
Nest
是一个上层框架,底层依赖于express
框架(或hapi),因此Nest
中middleware
等同于express
中的middleware
,咱们能够使用它来访问request
和response
,以及对处理程序作一些前置或后置操做。它的声明方式以下:bash
// 方式1
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class CatchMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('catch...');
next();
}
}
// 方式2
export function catchError(req, res, next) {
console.log(`catch...`);
next();
};
复制代码
// 方式1
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(CatchMiddleware)
.forRoutes(CatsController);
}
}
// 方式2
const app = await NestFactory.create(AppModule);
app.use(catchError);
await app.listen(3000);
复制代码
Nest
内置了处理程序未处理的异常捕获层(exception-filter
)以及友好的异常处理对象。咱们经过声明exception
来对catch
全局或局部的异常抛出,如全局的error
。以下面代码所示:app
http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
// @Catch()参数为空时,catch全部类型的异常
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
// 获取当前处理程序所在的上下文对象
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
// 拦截并处理响应
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
// 使用
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
复制代码
同时咱们还能够继承Nest
内置的exception
对象来规范化系统异常的格式,以下代码所示:
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
// 错误打印
{
"status": 403,
"error": "This is a custom message"
}
复制代码
Pipe
在Nest
中用于对controller
中处理程序的request
作前置处理。
exception
; 这里声明方式能够参考官方文档。在Nest
中,Guard
被设计拥有单一指责的前置处理程序,用于通知route
对应处理函数是否须要执行。官方文档中推荐使用Guard
来实现 authorization
相关的功能,如权限校验、角色校验等,来替代传统express
应用程序中使用middleware
实现的思路。
官方认为middleware
本质上是很“愚蠢”的,由于它没法知道在调用next
以后将执行哪个处理函数。而Guard
能够经过ExecutionContext
获取当前request
的执行上下文,清楚的知道哪个handler
将要执行,所以能够保证开发者在正确的request
或response
周期添加处理逻辑。
它的声明方式以下:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
// 注意函数声明
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
复制代码
同时guard
也支持全局、单个controller
或单个handle method
进行前置处理。详情能够参考官方文档。
在Nest
中,Interceptor
的设计灵感来自于[AOP](https://en.wikipedia.org/wiki/Aspect-oriented_programming)
,用于给某个处理函数模块(如controller
)添加前置/后置操做。实现功能如:输入/输出的额外操做、exception
的过滤、在特定条件下重载当前处理函数的逻辑等。它的声明方式以下:
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class HttpInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// context当前执行上下文
// next下一个将要执行的处理函数
return next.handle().pipe(
map((value: any) => ({
code: 0,
data: value,
}))
);
}
}
// 使用
UseInterceptors(HttpInterceptor)
export class CatsController {}
复制代码
以上介绍的各类Nest
组件被注册以后由Nest
框架管理,它们在一个请求的某个声明周期阶段被执行。具体执行顺序以下:
middleware
root module
)middleware
Guards
Controller Guards
route
对应处理函数上使用@UseGuards()
注册的Guard
interceptors
(controller前置处理)controller
的interceptors
(controller前置处理)route
对应处理函数上使用@UseInterceptors()
注册的interceptors
(controller前置处理)pipes
controller
的pipes
route
对应处理函数上使用@UsePipes()
注册的Pipes
route
对应处理函数参数注册的Pipes
(如:@Body(new ValidationPipe())route
对应处理函数route
对应处理函数依赖的Serivce
route
对应处理函数上使用@UseInterceptors()
注册的interceptors
(controller后置处理)controller
的interceptors
(controller后置处理)interceptors
(controller后置处理)route
对应处理函数注册的Exception filters
controller
注册的Exception filters
Exception filters