Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。
main.js入口程序
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
平台
nest是一个与平台无关的应用框架,它提供了两个支持开箱即用的 HTTP 平台:express 和 fastify.
const app = await NestFactory.create<NestExpressApplication>(AppModule);
或者:
const app = await NestFactory.create<NestFastifyApplication>(AppModule);
指定平台类型,可以获取底层api,一般不需要指定类型。
控制器
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return 'This action returns all cats';
}
}
路由
@Controller,定义匹配路由规则,路由返回格式有两种:
- 默认推荐格式;如果返回是对应,则nestj会将其序列化为json格式返回,如果是基本类型,则会直接进行返回。
- 与平台相关特有格式。可以通过返回函数上注入@res()特定平台平台的相应对象,从而使用其特有的一些方法,如:
response.status(200).send()等。
request
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
return 'This action returns all cats';
}
}
@Req()封装了底层的请求对象,可以使用此对象调用底层的一些api.
@Get()封装了请求方式,类似的还有@Put()`、`@Delete()`、`@Patch()`、`@Options()`、以及 `@Head()等·。
状态码
@Post()
@HttpCode(204)
create() {
return 'This action adds a new cat';
}
@HttpCode是nestjs提供的修改返回状态码的方式。我们也可以使用在请求方法参数中提供的@Res()来修改返回参数,但是一旦使用了@Res()这种特定于平台的方式,就无法试用nestjs为我们提供的HttpCode方式,但是可以通过设置passthroughs属性,你就可以既使用基于特定平台提供的api,又可以其他部分使用nestjs提供的方式处理:
@Get()
findAll(@Res({ passthrough: true }) res: Response) {
res.status(HttpStatus.OK);
return [];
}
headers
@Post()
@Header('Cache-Control', 'none')
create() { return 'This action adds a new cat'; }
可以使用@Header修改对应响应头,也可以使用@Res基于特定库对象的方式修改响应头
重定向
@Redirect('https://nestjs.com', 301)
这种方式能够实现请求的重定向,也可以使用返回特定格式的对象来达到动态重定向的目的,返回的值会覆盖@Redirect的值。
路由参数
@Get(':id')
findOne(@Param() params): string {
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
使用@Get(':id')来定义路由将要接受什么样的动态参数,使用@Param() params来获取动态路由参数id.
请求传入参数
/* create-cat.dto.ts */
export class CreateCatDto {
readonly name: string;
readonly age: number;
readonly breed: string;
}
/* cats.controller.ts */
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return 'This action adds a new cat';
}
@Body()可以处理请求传入参数的需求。
注册controller到modules中
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
@Module中配置controllers,nestjs可以轻松知道哪些控制器需要被安装。
提供者
定义provider类
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
provider类是一个用@Injectable修饰的类,可以注入到controller进行使用,而无需手动在controller中进行实例化。这里nestjs使用了依赖注入的设计模式。
controller中使用provider:
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
在modules中注册依赖者
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
模块
模块定义
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
模块是一个用@Module定义的类,里面定义了各种原数据,nest用它来组织应用结构。
模块提供了以下几个功能:
| providers | 由 Nest 注入器实例化的提供者,并且可以至少在整个模块中共享 |
|---|---|
| controllers | 必须创建的一组控制器 |
| imports | 导入模块的列表,这些模块导出了此模块中所需提供者 |
| exports | 由本模块提供并应在其他模块中可用的提供者的子集。 |
模块导入
@Module({
imports: [CatsModule],
})
export class ApplicationModule {}
我们可以将某一业务模块抽离出来,然后使用模块的imports导入来聚合模块。
共享模块
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
可以在exports中导出提供者,这样所有导入CatsModule的模块都可以共享相同的CatsService.
提供者也可以注入到模块中
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {
constructor(private readonly catsService: CatsService) {}
}
全局模块
@Global()
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
使@Global可以将模块注册为全局模块,在其他模块中使用CatsService,就不再需要导入CatsModule模块。
中间件
中间件是在路由处理程序 之前 调用的函数。 中间件函数可以访问请求和响应对象,以及应用程序请求响应周期中的 next() 中间件函数。
中间件定义
1.类定义中间件
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
- 类定义中间件需要使用
@Injectable()来修饰🥱,并且要实现NestMiddleware接口,重写use方法。 2.函数式中间件
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
中间件使用
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
}
}
模块中的configure()方法可以注入MiddlewareConsumer实例,也就是中间件消费者,可以使用此对象来配置中间件作用的规则。
- apply是指定应用的中间件,也可以应用多个中间件
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
- forRoutes指定中间件应用的路由。路由可以使用通配符,可以配置对用的http请求方式,只对满足配置的路由使用对应的中间件。
全局中间件
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
全局中间件可以将应用的中间件绑定到左右的注册路由上。
异常过滤器
异常过滤器会处理HttpException及其子类未被捕获的异常信息,将会以友好的形式返回错误的异常信息。 基础异常类
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
{
"statusCode": 403,
"message": "Forbidden"
}
- HttpException构造函数有两个参数分别为response,status,传入不同的值,可以覆盖返回的信息。 自定义异常类
nestjs提供了许多内置异常类,但是我们有时还是需要特殊的异常类:
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
自定义异常只需要继承已有的异常,并且传递对应需要修改的构造函数信息即可。 自定义异常过滤器
nestjs内置的异常处理器会自动的帮我们处理很多异常,但有时候希望对异常层拥有完全控制权,这个时候可以自定义我们自己的异常过滤器。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@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,
});
}
}
- @Catch是用来说明处理的异常类,如果参数为空,此异常处理器将会处理应用抛出的所有异常类,也可以传递多个参数,用逗号隔开。
- 自定义异常过滤器需要实现ExceptionFilter接口,并实现catch方法。
绑定过滤器
1.方法中绑定过滤器,则只会对此方法抛出的异常进行处理
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@UseFilters参数中可以使用类,也可以使用实例,最好使用类,这样全局就会共用同一个实例,节省内存。
2.绑定到类上。
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
3.全局绑定
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
全局过滤器用于整个应用程序、每个控制器和每个路由处理程序。