前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >五分钟带你入门基于Nodejs的强大的Web框架— NestJS

五分钟带你入门基于Nodejs的强大的Web框架— NestJS

作者头像
winty
发布2021-11-23 15:12:23
2.7K0
发布2021-11-23 15:12:23
举报
文章被收录于专栏:前端Q

简介

Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。在底层,Nest 使用强大的 HTTP Server 框架,如 Express(默认)和 Fastify。Nest 在这些框架之上提供了一定程度的抽象,同时也将其 API 直接暴露给开发人员。这样可以轻松使用每个平台的无数第三方模块。

支持 TypeScript(也支持纯 js 编写代码),默认支持最新的 ES6 等语法和特性(用 babel 做代码转换)。node 版本要求 >= 10.13.0, v13 版本除外

要了解 Nest ,建议先了解一下装饰器,因为 Nest 里面的方法很多都是以装饰器的方式提供的,下面我简单介绍一下。已经了解的朋友可以跳过~

装饰器

装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。它是一种函数,写成@ + 函数名的形式。它可以放在类和类方法的定义前面。

代码语言:javascript
复制
  @testable
  class MyTestableClass {
    // ...
  }
  function testable(target) {
    target.isTestable = true;
  }
  MyTestableClass.isTestable // true

基本上,装饰器的行为就是下面这样。

代码语言:javascript
复制
  @decorator
  class A {}
  // 等同于
  class A {}
  A = decorator(A) || A;

也就是说,装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要装饰的目标类。

注意点
  • 装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
  • 装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。如果一定要装饰函数,可以采用高阶函数的形式直接执行。

Nest 基本介绍

安装使用这里就不说了,可以到官网按照其引导来进行:https://docs.nestjs.com/first-steps。

生成的核心文件结构为:

代码语言:javascript
复制
src
  |-app.controller.spec.ts
  |-app.controller.ts
  |-app.module.ts
  |-app.service.ts
  |-main.ts

其代表的含义分别为:

文件

含义

app.controller.spec.ts

控制器的单元测试

app.controller.ts

控制器逻辑文件,通常含多个路由

app.module.ts

应用程序的根模块

app.service.ts

服务文件

main.ts

应用程序的入口文件,它是基于NestFactory创建的一个Nest应用程序实例

Controller

什么是 Controller?语义化翻译就是 控制器,它负责处理传入的请求并将响应结果返回给客户端。

Nest 中,控制器和路由机制是结合在一起的,控制器的目的是接收应用程序的特定请求。其路由机制控制哪个控制器接收哪些请求。通常,每个控制器都有多个路由,不同的路由可以执行不同的操作。

我们通过装饰器 @Controller() 来将一个类定义为控制器,如:

代码语言:javascript
复制
import { Controller } from '@nestjs/common';

@Controller('test')
export class TestController { }

Nest 把各个HTTP的请求方法都封装成了装饰器,如@Get()、@Post()、@Put()、@Patch()、@Delete()、@Options()等,因此我们在实际开发中,可以直接用来装饰对应的请求,比如以下几种路由:

代码语言:javascript
复制
import { Controller, Get, Post, Body, Put, Param, Delete } from '@nestjs/common';

@Controller('test')
export class TestController {
  @Post()
  async create(@Body() createTestDto: CreateTestDto) {
    return 'This action adds a new test';
  }

  @Delete(':id')
  async remove(@Param('id') id) {
    return `This action removes a #${id} test`;
  }

  @Put(':id')
  async update(@Param('id') id, @Body() updateTestDto: UpdateTestDto) {
    return `This action updates a #${id} test`;
  }

  @Get(':id')
  async findOne(@Param('id') id) {
    return `This action returns a #${id} test`;
  }
}

Provider

什么是 Provider?语义化翻译就是 提供者,在 Nest 中,除了控制器以外,几乎所有的东西都可以被视为提供者,比如service、repository、factory、helper等等。他们都可以通过构造函数注入依赖关系,也就是说,他们之间可以创建各种关系。而提供者只不过是一个用 @Injectable() 装饰器的简单类。

在类声明上,定义 @Injectable() 装饰器,即可将该类定义为提供者。如:

代码语言:javascript
复制
import { Injectable } from '@nestjs/common';
@Injectable()
export class TestService {
  private readonly test: Test[] = [];

  async create(createTestDto: CreateTestDto) {
    this.test.push(createTestDto);
  }

  async remove(id: number) {
    this.test.splice(test.indexOf(test.find(t => t.id === id)), 1);
  }

  async update(id: number, updateTestDto: UpdateTestDto) {
      if(updateTestDto.name) this.test.find(t => t.id === id).name = updateTestDto.name;
  }

  async findOne(id: number): Test {
      return this.test.find(t => t.id === id);
  }
}

Module

Module也是一个装饰器,Nest 使用 Module来组织应用程序结构,每个应用程序至少有一个模块,即根模块。根模块是 Nest 开始排列应用程序树的地方。当应用程序很小时,根模块可能是应用程序中唯一的模块。不过,大多数情况下,都有很多模块,每个模块都有一组与其密切相关的功能。

模块,是用来组织 ControllerProvider,为他们在 同模块范围内 建立依赖关系的。比如上面的 ControllerProvider,我们建立关系:

代码语言:javascript
复制
import { Module } from '@nestjs/common';
import { TestController } from './test.controller';
import { TestService } from './test.service';

@Module({
  controllers: [TestController],
  providers: [TestService],
})
export class TestModule {}

只有这样,Nest 才可以在 TestController 中通过其构造函数,依赖注入 TestService,才可以在 controller 中调用 service 服务。

而当不同模块之间的服务需要互相调用时,我们就要在对应的模块之间导出和导入了,例如:

代码语言:javascript
复制
import { Module } from '@nestjs/common';
import { TestController } from './test.controller';
import { TestService } from './test.service';

@Module({
  imports: [],
  controllers: [TestController],
  providers: [TestService],
  exports: [TestService]
})
export class TestModule {}
全局模块

如果你必须在很多地方都导入相同的模块,这会出现大量的冗余。但是 Nest 将提供者封装在模块范围内,如果不导入模块,就无法在其他地方使用他们导出的提供者。但是有时候,你可能只是想提供一组随时可用的提供者,例如:helpers、database connection 等等。针对这种特殊情况,Nest 提供了一个很强大的功能 —— 全局模块,全局模块一旦被导入到根模块,在其他所有模块中即可轻松的使用这个全局模块导出的提供者,而且也不用在其他模块导入这个全局模块。

将一个模块定义为全局模块,只需要在类上额外增加一个装饰器 @Global() 即可,示例:

代码语言:javascript
复制
import { Module, Global } from '@nestjs/common';

@Global()
@Module({
  imports: [],
  controllers: [],
  providers: [],
  exports: []
})
export class TestModule {}
动态模块

Nest 模块系统有一个称为动态模块的特性。它能够让我们创建可定制的模块,当导入模块并向其传入某些选项参数,这个模块根据这些选项参数来动态的创建不同特性的模块,这种通过导入时传入参数并动态创建模块的特性称为 动态模块

代码语言:javascript
复制
@Module({})
export class AppModule {
  static register(CustomController): DynamicModule {
    return {
      module: AppModule,
      controllers: [CustomController],
      providers: [],
      exports: [],
    };
  }
}

NestFactory

在 Nest 中,我们通过在 main 入口中调用 NestFactory.create 来创建 Nest 应用实例,Nest 创建的实例默认是 express 实例。main 入口示例:

代码语言:javascript
复制
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  // 使用 NestFactory 创建一个根模块为 AppModule 的 Nest app
  const app = await NestFactory.create(AppModule);
  // 将这个 Nest app 监听本地的 3000 端口,即:http://localhost:3000
  await app.listen(3000);
}
bootstrap();

Middleware

Middleware 即中间件,它是请求发出者和路由处理器之间的桥梁,可以透明的、轻松的访问请求和响应对象。在 Nest 中,中间件可以有多个,他们之间使用 next() 方法作为连接,连接后的所有中间件将在整个请求-响应周期内通过 next()依次执行。

注:默认情况下,Nest 中间件等同于 Express 中间件。Nest 中间件可以是一个函数,也可以是一个带有 @Injectable() 装饰器的类,且该类应该实现 NestMiddleware 接口,而函数没有任何特殊要求。如下简单示例:

代码语言:javascript
复制
// 带有 `@Injectable()` 装饰器的类中间件
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class OAAuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...: ', req);
    next();
  }
}
// 函数中间件
export function OAAuthMiddleware(req, res, next) {
  console.log('res: ', res);
  next();
}

ProviderController一样,中间件也能够通过构造函数注入属于同一模块的依赖项。

全局中间件使用

为了将中间件一次性绑定到每个注册的路由,我们可以通过 Nest 实例中的 use() 方法使用:

代码语言:javascript
复制
const app = await NestFactory.create(ApplicationModule);
// 这里必须使用函数中间件
app.use(OAAuthMiddleware);
await app.listen(3000);
模块中使用

既然中间件是请求发出者和路由处理器之间的桥梁,那么他就应该在一个模块的入口,即 XXXModule 类中被使用。在 Nest 中,我们只需要在模块类中实现 NestModule 接口:

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { OAAuthMiddleware } from './common/middlewares/oAAuthMiddleware.middleware';
import { TestModule } from './test/test.module';

@Module({
  imports: [TestModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(OAAuthMiddleware)
      .forRoutes('test');
  }
}

在上面的例子中,我们为 /test 路由处理器 (@TestController('/test')) 设置了鉴权中间件。如果只需要给 /test 路由中的某几个请求方法设置这个中间件,那只需要改变一下 forRoutes() 方法中的参数即可:forRoutes({ path: 'test', method: RequestMethod.GET }),此时,只有 GET 请求才会被中间件拦截。如果存在很多路由规则,也可以使用通配符来处理。如:

代码语言:javascript
复制
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })

而当你想排除一个控制器类中的某些路由不使用中间件时,使用 exclude() 方法即可,如:

代码语言:javascript
复制
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { OAAuthMiddleware } from './common/middlewares/oAAuthMiddleware.middleware';
import { TestModule } from './test/test.module';

@Module({
  imports: [TestModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(OAAuthMiddleware)
      .exclude(
        { path: 'test', method: RequestMethod.GET },
      )
      .forRoutes('test');
  }
}

最后

了解了 Nest 的基本概念之后,可以安装@nestjs/cli来体验一下 Nest 项目,这里给大家出个思考题,如何把 Nest 项目抽离为 runtime(Nest框架) + faas(入口文件) 的形式呢?

参考资料

  • https://www.bookstack.cn/read/es6-3rd/docs-decorator.md
  • https://docs.nestjs.com
  • http://ourjs.com/wiki/view/nestjs/02.controller
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端Q 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 装饰器
    • 注意点
    • Nest 基本介绍
    • Controller
    • Provider
    • Module
      • 全局模块
        • 动态模块
        • NestFactory
        • Middleware
          • 全局中间件使用
            • 模块中使用
            • 最后
            • 参考资料
            相关产品与服务
            消息队列 TDMQ
            消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档