Nestjs学习
命令
创建项目
npx @nestjs/cli new 项目名称生成module、controller、service模块
nest generate module aaa
nest generate controller aaa
nest generate service aaa
aaa-文件夹名称完整生成一个模块的代码
nest generate resource xxxnest 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 创建一个路由中间件:
nest g middleware log --no-spec --flat--no-spec 是不生成测试文件,--flat 是平铺,不生成目录。
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 里启用:
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:
nest g guard login --no-spec --flat生成的代码:
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 里启用:
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 也可以全局启用:
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 里这样声明:
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:
nest g interceptor time --no-spec --flat生成的 interceptor 是这样的:
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 里启用:
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:
nest g pipe validate --no-spec --flat//生成代码
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:
nest g filter test --no-spec --flat生成代码:
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中使用
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