初识 Nest.js

Nest.js官网介绍:

Nest (NestJS) 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的开发框架。它利用JavaScript 的渐进增强的能力,使用并完全支持 TypeScript (仍然允许开发者使用纯 JavaScript 进行开发),并结合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。

在底层,Nest 构建在强大的 HTTP 服务器框架上,例如 Express (默认),并且还可以通过配置从而使用 Fastify !

Nest 在这些常见的 Node.js 框架 (Express/Fastify) 之上提高了一个抽象级别,但仍然向开发者直接暴露了底层框架的 API。这使得开发者可以自由地使用适用于底层平台的无数的第三方模块。

上面这段话刚开始并不能完全理解, 但是简单可以解读出来Nest.js的几个特点:

  • 原生支持TypeScript的框架
  • 可以基于Express也可以选择fastify, 如果你对Express非常熟练, 直接用它的API也是没问题的

至于其他看不懂,就暂时放一边, 因为不影响我们入门,后面深入学习后会再来分析。

为什么选择nest.js?

它通过灵活使用控制反转、依赖注入和面向切面编程等设计理念,极大的规范了大型应用的架构,降低了模块之间的耦合度,从而提升了应用的开发效率。在 NodeJS 的世界里,也存在一个全面借鉴 Spring 设计思想的框架

nest与egg简单对比

  • 都是为企业级框架和应用而生
  • Egg.js基于Koa,Nest.js基于express
  • Egg.js和Nest.js都是按照约定进行开发,Egg相比Nest约定更标准
  • 面向对象方面,Nest.js优于Egg.js

Egg特性:

  • 高度可扩展的插件
  • 内置多进程管理
  • 基于Koa,性能优异
  • 框架稳定,测试覆盖率高

Nest特性

  • 依赖注入容器
  • 模块化封装

项目创建

首先确定你已经安装了Node.js, Node.js 安装会附带npx和一个npm 包运行程序。要创建新的Nest.js 应用程序,请在终端上运行以下命令:

npm i -g @nestjs/cli  // 全局安装Nest
nest new project-name  // 创建项目

执行完创建项目, 会初始化下面这些文件, 并且询问你要是有什么方式来管理依赖包:

nestjs整合nacos nestjs api_HTTP

如果你有安装yarn,可以选择yarn,能更快一些,npm在国内安装速度会慢一些

nestjs整合nacos nestjs api_应用程序_02

接下来按照提示运行项目:

nestjs整合nacos nestjs api_nestjs整合nacos_03

 

 

注意: Nest.js 要求 Node.js(>= 10.13.0,v13 除外), 如果你的Node.js 版本不满足要求,可以通过nvm包管理工具安装符合要求的Node.js版本

 

项目结构

进入项目,看到的目录结构应该是这样的:

nestjs整合nacos nestjs api_nestjs整合nacos_04

 

 

 这里简单说明一下这些核心文件:

src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── main.ts

  

app.controller.ts

单个路由的基本控制器(Controller)

app.controller.spec.ts

针对控制器的单元测试

app.module.ts

应用程序的根模块(Module)

app.service.ts

具有单一方法的基本服务(Service)

main.ts

应用程序的入口文件,它使用核心函数 NestFactory 来创建 Nest 应用程序的实例。



 

 

 

 

第一个接口

前面我们已经启动了服务, 那我们怎么查看呢, 首先就是找到入口文件main.ts

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.js的工厂函数NestFactory来创建了一个AppModule实例,启动了 HTTP 侦听器,以侦听main.ts 中所定义的端口。

我们打开浏览器访问http://localhost:3000地址:

nestjs整合nacos nestjs api_HTTP_05

 

 这里看到的Hello World就是接口地址http://localhost:9080返回的内容

说明Nest.js创建项目默认就给写了一个接口例子,那就通过这个接口例子来看,我们应该怎么实现一个接口。

前边看到mian.ts中也没有别的文件引入, 只有AppModule, 打开src/app.module.ts:

nestjs整合nacos nestjs api_拦截器_06

 

 

AppModule是应用程序的根模块,根模块提供了用来启动应用的引导机制,可以包含很多功能模块。

.mudule文件需要使用一个@Module() 装饰器的类,装饰器可以理解成一个封装好的函数,其实是一个语法糖,@Module() 装饰器接收四个属性:providerscontrollersimportsexports

  • providers:Nest.js注入器实例化的提供者(服务提供者),处理具体的业务逻辑,各个模块之间可以共享(注入器的概念后面依赖注入部分会讲解);
  • controllers:处理http请求,包括路由控制,向客户端返回响应,将具体业务逻辑委托给providers处理;
  • imports:导入模块的列表,如果需要使用其他模块的服务,需要通过这里导入;
  • exports:导出服务的列表,供其他模块导入使用。如果希望当前模块下的服务可以被其他模块共享,需要在这里配置导出;

因为咱们平时公司技术栈是vue,会觉得面生,会写AngularJS应该比较熟悉,后端写过java也应该比较熟悉,像极了Spring boot,AngularJSSpringNest.js都是基于控制反转原则设计的,而且都使用了依赖注入的方式来解决解耦问题

 

咱们接着看文件,在app.module.ts中,看到它引入了app.controller.tsapp.service.ts

nestjs整合nacos nestjs api_HTTP_07

使用@Controller装饰器来定义控制器, @Get是请求方法的装饰器,对getHello方法进行修饰, 表示这个方法会被GET请求调用。

 

nestjs整合nacos nestjs api_应用程序_08

 

 

从上面,我们可以看出使用@Injectable修饰后的 AppService, 在AppModule中注册之后,在app.controller.ts中使用,我们就不需要使用new AppService()去实例化,直接引入过来就可以用。

至此,对于http://localhost:3000/接口返回的Hello World逻辑就算理清楚了, 在这基础上我们再详细的学习一下Nest.js中的路由使用。

路由装饰器

Nest.js中没有单独配置路由的地方,而是使用装饰器。Nest.js中定义了若干的装饰器用于处理路由。

@Controller

如每一个要成为控制器的类,都需要借助@Controller装饰器的装饰,该装饰器可以传入一个路径参数,作为访问这个控制器的主路径:

app.controller.ts文件进行修改

nestjs整合nacos nestjs api_拦截器_09

 

 通过@Controller("api")修改这个控制器的路由前缀为api, 此时可以通过

nestjs整合nacos nestjs api_应用程序_10

 

 

HTTP方法处理装饰器

@Get@Post@Put等众多用于HTTP方法处理装饰器,经过它们装饰的方法,可以对相应的HTTP请求进行响应。同时它们可以接受一个字符串或一个字符串数组作为参数,这里的字符串可以是固定的路径,也可以是通配符。现在我们来两个简单的例子

nestjs整合nacos nestjs api_HTTP_11

app.service

 

nestjs整合nacos nestjs api_拦截器_12

 

nestjs整合nacos nestjs api_HTTP_13

 

 

 

nestjs整合nacos nestjs api_nestjs整合nacos_14

 

 

全局路由前缀

除了上面这些装饰器可以设置路由外, 我们还可以设置全局路由前缀, 比如给所以路由都加上/api前缀。此时需要修改main.ts

nestjs整合nacos nestjs api_HTTP_15

 

 之前在Controller声明的地址就不用写了,到此我们认识了ControllerServiceModule、路由以及一些常用的装饰器

 

介绍几个nest-cli提供的几个有用的命令


//语法
nest g [文件类型] [文件名] [文件目录]

  

创建模块 

nest g mo posts

创建一个 posts模块,文件目录不写,默认创建和文件名一样的posts目录,在posts目录下创建一个posts.module.ts

执行完命令后,我们还可以发现同时在根模块app.module.ts中引入PostsModule这个模块,也在@Model装饰器的inports中引入了PostsModule

创建控制器

nest g co posts

此时创建了一个posts控制器,命名为posts.controller.ts以及一个该控制器的单元测试文件

执行完命令, 文件posts.module.ts中会自动引入PostsController,并且在@Module装饰器的controllers中注入。

创建服务类

nest g service posts

创建app.service.ts文件,并且在app.module.ts文件下,@Module装饰器的providers中注入注入

 

其实nest-cli提供的创建命令还有很多, 比如创建过滤器、拦截器和中间件等,可以去官网查看命令

 

接口格式统一

一般开发中是不会根据HTTP状态码来判断接口成功与失败的, 而是会根据请求返回的数据,里面加上code字段

首先定义返回的json格式:

{
    "code": 0,
    "message": "OK",
    "data": {
    }
}

  请求失败时返回:

"code": -1,
    "message": "我错了",
    "data": {}
}

  

拦截错误请求

首先使用命令创建一个过滤器:

nest g filter core/filter/http-exception

过滤器代码实现:

import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp(); // 获取请求上下文
    const response = ctx.getResponse(); // 获取请求上下文中的 response对象
    const status = exception.getStatus(); // 获取异常状态码

    // 设置错误信息
    const message = exception.message
      ? exception.message
      : `${status >= 500 ? 'Service Error' : 'Client Error'}`;
    const errorResponse = {
      data: {},
      message,
      code: 200,
    };

    // 设置返回的状态码, 请求头,发送错误信息
    response.status(200);
    response.header('Content-Type', 'application/json; charset=utf-8');
    response.send(errorResponse);
  }
}

  最后需要在main.ts中全局注册

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api'); // 设置全局路由前缀
  app.useGlobalFilters(new HttpExceptionFilter())
  
  await app.listen(3000);
}

  

这样对请求错误就可以统一的返回了,返回请求错误只需要抛出异常即可,比如之前的:

throw new HttpException('抛出', 200);

 

nestjs整合nacos nestjs api_nestjs整合nacos_16

 

接下来对请求成功返回的格式进行统一的处理,可以用Nest.js的拦截器来实现。

 

拦截成功的返回数据

首先使用命令创建一个拦截器:

 

nest g interceptor core/interceptor/transform

  拦截器代码实现:

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

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data) => {
        return {
          data,
          code: 200,
          msg: '请求成功',
        };
      }),
    );
  }
}

  最后和过滤器一样,在main.ts中全局注册:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api'); // 设置全局路由前缀
  app.useGlobalInterceptors(new TransformInterceptor())
  app.useGlobalFilters(new HttpExceptionFilter())
  
  await app.listen(3000);
}

过滤器和拦截器实现都是三部曲:创建 > 实现 > 注册,还是很简单的

总结

至此我们Nest.js快速上手入门就告一段落了,文章从项目如何搭建,到实现简单的CRUD,再到统一接口格式、完成接口参数验证