nestjs(8.x版本)

471 阅读7分钟

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模块。

中间件

image.png

中间件是在路由处理程序 之前 调用的函数。 中间件函数可以访问请求和响应对象,以及应用程序请求响应周期中的 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();

全局过滤器用于整个应用程序、每个控制器和每个路由处理程序。