Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。
安装
$ npm i -g @nestjs/cli
$ nest new project-name
文件入口
/* main.ts */
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
await app.listen(3000);
}
bootstrap();
Controllers 控制器
/* cats.controller.ts */
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';
}
}
GET 请求
- @Query(key?: string) 传参数
localhost:3000/example?id='1'
@Get('example')
async getData(@Query() query: QueryDto): string {
console.log(query)
return ''
}
- 动态参数 @Param()
localhost:3000/example?id='1'
@Get('example')
async getData(@Param() param: ParamDto): string {
console.log(param)
return ''
}
POST请求
Content-Type
含义及其作用
一个正确的HTTP请求应当具备状态行、请求头、消息主体,数据本身的编码方式由Content-Type声明,请求的客户端和服务端必须遵守同一个编码方式才能正确解析数据的内容
类型1:Content-Type:application/x-www-form-urlencoded
—常用于原生的 form 表单提交
类型2:Content-Type:multipart/form-data
—常用于文件传输
类型3:Content-Type:application/json
—以Json格式编码数据体,方便传输结构化数据(键值对),广泛用于 ajax 请求
请求头 Content-Type:application/x-www-form-urlencoded
method: POST
@Post('create')
async create(@Body() createUserData: CreateUserData, @Headers() headers: any): string{
console.log(createUserData,headers)
return ''
}
Providers 服务提供者
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;
}
}
Moudles 模块
全局模块 @Blobal() 和模块配置 configModule
一、在项目config目录导出你需要的配置文件
export default () => {
return {
url: process.env.DATABASE_URL
}
}
二、在模块的imports 里面配置文件的异步导入
import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { PostSchema, Post } from '../schema/post.schema';
import { User, UserSchema } from '../schema/user.schema';
import { MongoService } from './mongo.service';
const MongoConfig = MongooseModule.forRootAsync({
imports: [ConfigModule.forRoot({ isGlobal: true })],
useFactory: async (configService: ConfigService) => {
return {
uri: configService.get<string>('MONGODB_DATABASE_URL')
}
},
inject: [ConfigService]
})
const Models = MongooseModule.forFeature([
{ name: User.name, schema: UserSchema },
{ name: Post.name, schema: PostSchema },
])
@Global()
@Module({
imports: [
MongoConfig,
Models,
],
providers: [MongoService],
exports: [MongoModule, Models,MongoService ]
})
export class MongoModule { }
三、注意配置了全局模块后,还需要在appModules 模块里面引用全局模块
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongoModule } from './database/mongo/mongo.module';
@Module({
imports: [
MongoModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
四、在服务和控制器里面要想使用全局模块,需要使用依赖注入的方式.
@Injectable()
export class UserService {
constructor(
@InjectModel(User.name) private readonly userDao: Model<userModel>,
private readonly mongoService: MongoService
) { }
}
注册模块 要在providers里面注册 providers[CatsService]
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
共享模块 要在exports里面导出要共享的模块 exports: [CatsService]
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
- controllers: 该模块依赖的所有控制器
- providers: 该模块所依赖的所有服务提供内容
- exports: 道出该模块,其他模块要用该模块的时候,直接引入该模块
模块的依赖注入 constructor(private readonly catsService: CatsService) {}
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {
constructor(private readonly catsService: CatsService) {}
}
动态模块
Middleware 中间件
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();
}
}
应用中间件
我们必须使用模块类的 configure()
方法来设置它们。包含中间件的模块必须实现 NestModule
接口。我们将 LoggerMiddleware 设置在 ApplicationModule 层上。
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('cats');
}
}
路由通配符 forRoutes (里面是控制器路由)
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
异常过滤器
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,
});
}
}
管道
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Validation failed');
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
- 使用 管道
new ValidationPipe()
@Post()
async create(@Body(new ValidationPipe()) createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@UsePipes()
@Post()
@UsePipes(new ValidationPipe())
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
- 全局管道
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe
}
]
})
export class AppModule {}
守卫
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);
}
}
基于角色认证
一个更详细的例子是一个 RolesGuard 。这个守卫只允许具有特定角色的用户访问。我们将从一个基本模板开始,并在接下来的部分中构建它。目前,它允许所有请求继续:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true;
}
}
绑定守卫
与管道和异常过滤器一样,守卫可以是控制范围的、方法范围的或全局范围的。下面,我们使用 @UseGuards()装饰器设置了一个控制范围的守卫。这个装饰器可以使用单个参数,也可以使用逗号分隔的参数列表。也就是说,你可以传递几个守卫并用逗号分隔它们。
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}
- 全局守卫
app.useGlobalGuards
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
绑定拦截器
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
- 全局拦截器
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}