Skip to content

Nestjs学习

命令

创建项目

shell
npx @nestjs/cli new 项目名称

生成module、controller、service模块

shell
nest generate module aaa
nest generate controller aaa
nest generate service aaa
aaa-文件夹名称

完整生成一个模块的代码

shell
nest generate resource xxx

nest generate 选项:

​ --flat 和 --no-flat 是指定是否生成对应目录的

​ --spec 和 --no-spec 是指定是否生成测试文件

​ --skip-import 是指定不在 AppModule 里引入

子命令:

  • nest new 快速创建项目

  • nest generate 快速生成各种代码

  • nest build 使用 tsc 或者 webpack 构建代码

  • nest start 启动开发服务,支持 watch 和调试

  • nest info 打印 node、npm、nest 包的依赖版本

中间件

中间件是 Express 里的概念,Nest 的底层是 Express,所以自然也可以使用中间件,但是做了进一步的细分,分为了全局中间件和路由中间件。可以给在 handler 前后动态增加一些可复用的逻辑

用 nest cli 创建一个路由中间件:

shell
nest g middleware log --no-spec --flat

--no-spec 是不生成测试文件,--flat 是平铺,不生成目录。

ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class LogMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => void) {
    console.log('before2', req.url);

    next();

    console.log('after2');
  }
}

然后在 AppModule 里启用:

ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LogMiddleware } from './log.middleware';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule{

  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LogMiddleware).forRoutes('aaa*');
  }

}

Guard路由守卫

Guard 是路由守卫的意思,可以用于在调用某个 Controller 之前判断权限,返回 true 或者 false 来决定是否放行:

创建个 Guard:

shell
nest g guard login --no-spec --flat

生成的代码:

ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class LoginGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('login check')
    return false;
  }
}

Guard 要实现 CanActivate 接口,实现 canActivate 方法,可以从 context 拿到请求的信息,然后做一些权限验证等处理之后返回 true 或者 false。

我们加个打印语句,然后返回 false。

在 AppController 里启用:

ts
import { Controller, Get, Query, UseFilters, UseGuards, UseInterceptors, UsePipes } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

@Controller()
// @UseInterceptors(TimeInterceptor)
@UsePipes(ValidatePipe)
export class AppController {
  constructor(private readonly appService: AppService) {}
    
  @Get('bbb')
  @UseGuards(LoginGuard)
  bbb(): string {
    console.log('bbb...');
    return 'bbb';
  }


}

Guard 也可以全局启用:

ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NextFunction, Request, Response } from 'express';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.use(function(req: Request, res: Response, next: NextFunction) {
    console.log('before', req.url);
    next();
    console.log('after');
  })

  // app.useGlobalInterceptors(new TimeInterceptor())
   app.useGlobalGuards(new LoginGuard())
  // app.useGlobalPipes(new ValidatePipe());
  // app.useGlobalFilters(new TestFilter());
  await app.listen(3000);
}
bootstrap();

还有一种全局启用的方式,是在 AppModule 里这样声明:

ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LogMiddleware } from './log.middleware';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: LoginGuard
     },
    {
      provide: APP_INTERCEPTOR,
      useClass: TimeInterceptor
    },
    {
      provide: APP_PIPE,
      useClass: ValidatePipe
    },
    {
      provide: APP_FILTER,
      useClass: TestFilter
    },
  ],
})
export class AppModule implements NestModule{

  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LogMiddleware).forRoutes('aaa*');
  }
}

interceptor 拦截器

Interceptor 是拦截器的意思,可以在目标 Controller 方法前后加入一些逻辑:

创建个 interceptor:

shell
nest g interceptor time --no-spec --flat

生成的 interceptor 是这样的:

ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class TimeInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {

    const startTime = Date.now();

    return next.handle().pipe(
      tap(() => {
        console.log('time: ', Date.now() - startTime)
      })
    );
  }
}

Controller 之前之后的处理逻辑可能是异步的。

在 AppController 里启用:

ts
import { Controller, Get, Query, UseFilters, UseGuards, UseInterceptors, UsePipes } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

@Controller()
@UsePipes(ValidatePipe)
export class AppController {
  constructor(private readonly appService: AppService) {}
    
  @Get('bbb')
  @UseInterceptors(TimeInterceptor)
  bbb(): string {
    console.log('bbb...');
    return 'bbb';
  }


}

Pipe管道

Pipe 是管道的意思,用来对参数做一些检验和转换:

用 nest cli 创建个 pipe:

shell
nest g pipe validate --no-spec --flat
ts
//生成代码
import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class ValidatePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {

    if(Number.isNaN(parseInt(value))) {
      throw new BadRequestException(`参数${metadata.data}只能是字符串或者数字`)
    }

    return typeof value === 'number' ? value * 10 : parseInt(value) * 10;
  }
}

Nest 内置了一些 Pipe:

  • ValidationPipe
  • ParseIntPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • DefaultValuePipe
  • ParseEnumPipe
  • ParseFloatPipe
  • ParseFilePipe

ExceptionFilter

ExceptionFilter 可以对抛出的异常做处理,返回对应的响应

创建一个 filter:

shell
nest g filter test --no-spec --flat

生成代码:

ts
import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
import { Response } from 'express';

@Catch(BadRequestException)
export class TestFilter implements ExceptionFilter {
  catch(exception: BadRequestException, host: ArgumentsHost) {

    const response: Response = host.switchToHttp().getResponse();

    response.status(400).json({
      statusCode: 400,
      message: 'test: ' + exception.message
    })
  }
}

在controller中使用

ts
import { Controller, Get, Query, UseFilters, UseGuards, UseInterceptors, UsePipes } from '@nestjs/common';
import { AppService } from './app.service';
import { LoginGuard } from './login.guard';
import { TimeInterceptor } from './time.interceptor';
import { ValidatePipe } from './validate.pipe';
import { TestFilter } from './test.filter';

@Controller()
// @UseInterceptors(TimeInterceptor)
@UsePipes(ValidatePipe)
export class AppController {
  constructor(private readonly appService: AppService) {}
    
  @Get('bbb')
  @UseFilters(TestFilter)
  bbb(): string {
    console.log('bbb...');
    return 'bbb';
  }


}

Nest 内置了很多 http 相关的异常,都是 HttpException 的子类:

  • BadRequestException
  • UnauthorizedException
  • NotFoundException
  • ForbiddenException
  • NotAcceptableException
  • RequestTimeoutException
  • ConflictException
  • GoneException
  • PayloadTooLargeException
  • UnsupportedMediaTypeException
  • UnprocessableException
  • InternalServerErrorException
  • NotImplementedException
  • BadGatewayException
  • ServiceUnavailableException
  • GatewayTimeoutException

nest装饰器

module相互引用和service相互注入的时候使用 forwardRef