Nest.js口袋书(4)src目录

欢迎订阅Nest.js口袋书专栏

如果您觉得这篇文章有帮助的话!给个点赞和评论支持下吧,感谢~

作者:前端小王hs

阿里云社区博客专家/清华大学出版社签约作者✍/者作问访万百NDSC/B站千粉前端up主

核心文件

第三节我们提到了src是项目的核心目录,其包含的文件如下所示:

那么现在,我们逐一来分析里面的内容,并最后总结整个过程

当然,这里需要提一嘴的是,在官方的中文文档 中,也有对src目录的文件介绍,所以如果读者控制器根模块服务等概念熟悉的话,可以跳过这一节内容。介绍如下图所示:

app.controller.spec.ts

这是一个基于Jest测试文件 ,其内容与在根目录下test目录中的app.e2e-spec.ts逻辑相同,主要用于测试app.controller.ts控制器中定义的路由处理逻辑的单元测试文件

这里需要注意的是,带有.spec.ts后缀的文件,在nest中就是用于测试的文件,这是一种约定俗成的命名方式

同时,在本节中笔者将不会对该文件进行分析,但等我们熟悉了请求的逻辑是如何被响应及处理时,笔者将会重新讲解如何使用Jest进行测试

app.controller.ts

这是一个简单的控制器 示例,controller的翻译即为控制器

那么我们该如何去理解控制器 的作用?如果您了解过express框架,并对express框架有一定的代码实践,那么应该对路由路由处理程序 熟悉,而控制器 即对应路由

如果您没了解过express,可查看笔者在去年发布的vue3+ts+mysql+express+源码+实战+全栈|后台管理系统项目项目

我们知道,在express中的路由 的作用是负责处理传入的HTTP请求并返回响应,而控制器也是起到同样的作用

我们来看一下控制器中的代码

ts 复制代码
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

第一行代码从@nestjs/common模块中导入了两个装饰器,分别是ControllerGet

@nestjs/common模块是nest框架的核心模块,提供了大量的用于构建nest应用程序的基础装饰器、类、实用程序、异常和错误 等,可以说在后面的代码实践中,我们也会多次的使用该模块去导入基础装饰器、类、实用程序、异常和错误

由于我们的口袋书内容是循序渐进,由浅至深 的,所以笔者会在使用到某个装饰器或其他导入内容时再对其进行介绍

这里我们需要知道的是,nest中使用@Controller标识这是一个控制器

假设我们要定义一个包含多个路由路由模块 ,在express中的代码如下所示:

ts 复制代码
// 登录注册模块路由
// 导入express框架
const express = require('express')
// 使用express框架的路由
const router = express.Router()
// 导入login的路由处理程序模块
const loginHandler = require('../router_handle/login')
// 注册
router.post('/register',loginHandler.register)
// 登录
router.post('/login',loginHandler.login)
// 向外暴露路由
module.exports = router

而在nest中,就需要导入Controller并进行标识

上述express的代码示例中,我们可以看到每个路由 的都是使用了router.post()方法,这是规定了该路由的HTTP请求方式为POST。而在我们当前的控制器 文件中,是通过装饰器 标识该路由的请求方式,如代码中的@Get,即标识该路由的请求方式为GET

此外,我们在express的代码示例中,可看到router.post()方法的参数除了请求路径 外,还有loginHandler.register(以注册为例),这是路由的处理程序 (执行逻辑区域),是从../router_handle/login中导入的

我们可以想象,在../router_handle/login中的代码如下所示:

ts 复制代码
exports.register = (req, res) => {
   // 注册逻辑,如判断当前账号是否已存在、对密码加密等
}

"王哥,现在我知道了这个控制器 就是express的路由,但是在B站的项目里,注册接口是有前缀的,如/api/register,这些路径写在哪呢?"

"真是个好问题,如果我们想要在nest中给接口添加该模块 的前缀(假设登录模块的前缀为/api),并且给指定的路由添加前缀(假设注册功能的前缀为/register),可以这样写:

ts 复制代码
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller('api')  
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Post('register')  
  register(): string {
    return this.appService.register();
  }
}

那么这样,只有当请求方式为post且路径为/api/register时,才会执行注册逻辑 "

而在nest中,路由的处理程序位于service中,可以看到在第二行代码中导入了该控制器 对应的路由处理程序AppService,并在@Get请求中调用了位于./app.servicegetHello()方法,由于该方法返回的是一段字符串 ,所以添加了返回类型string

现在,我们明白了一个HTTP请求会途径控制器 ,并在服务中找到对应的处理程序

但我们还有一段代码没有分析,就是

ts 复制代码
constructor(private readonly appService: AppService) {}

这一段代码使用了TypeScript的类构造函数、privatereadonly两个修饰符,以及DI (依赖注入)概念,关于这一点,我们会在讲解service时进行解释

码字不易,如果觉得本文章不错,还请添加订阅并点赞喔~

app.service.ts

我们还是直接先来分析这段代码,代码如下:

ts 复制代码
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

第一行代码中,从nest的核心模块@nestjs/common中导入了Injectable,该单词意为可注射/注入的,意味这这个向外导出的 是一个可注入的 ,那么问题来了,注入到哪?

非常简单,我们回想在哪里导入了该AppService?没错,在控制器 中,并在constructor这个类构造函数中进行了注入 操作,那么问题又来了,注入的作用是什么?

我们知道面向对象开发的类与对象 概念,类就是一个模板 ,而对象模板的一个具体实现,就好比做月饼需要有个模具,不同的模具能够做出不同样式的月饼

TavaScript中,我们通常会使用如下代码去实现一个实例

ts 复制代码
class Person {  
    name: string;  
    age: number;  
  
    constructor(name: string, age: number) {  
        this.name = name;  
        this.age = age;  
    }  
  
    greet() {  
        console.log(`我的名字是 ${this.name},我今年 ${this.age} 岁.`);  
    }  
}  
  
// 使用new关键字和构造函数"手动"创建 Person 类的一个实例  
const person = new Person("前端小王hs", 24);  
  
// 调用实例的方法  
person.greet(); // 输出: 我的名字是前端小王hs,今年24岁.

而在nest中,我们无需手动的创建一个实例,这是由依赖注入容器来完成的

回看源码的控制器 ,是通过参数的形式将这个 AppService作为注入的依赖的类型(模板),创建了一个名为appService的实例,那么理所当然的是该实例具有了getHello()方法,进而可以直接在控制器中进行调用类中定义的方法

而对于控制器 中的private,则表示这个实例appService,只能在当前类(AppController)中访问;readonly则表示实例appService如果在类AppService中已经被赋值,就不能重新赋值了。这样做的好处是保证service的不可变性,增加代码的稳定性

对于AppService中的逻辑,就非常简单了,就是定义了一个方法getHello()

app.module.ts

从名字带有module可知,这是本程序的根模块 。在nest中的开发是使用模块化的方式组织代码,每个模块都包含一组(数组形式控制器(controllers)提供者(providers) 、以及其他模块,也就是下列代码@Module中的三个参数

ts 复制代码
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

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

"怎么哪都有你------nestjs/common"

"因为我是核心模块,嘿嘿"

AppModule中需要使用Module装饰器来定义模块的结构,Module即意为"模块"

在这段代码中,清晰地告诉了nest谁是控制器 和谁是提供者 ,当nest启动时,就会根据当前@Module中的结构去进行初始化。在复杂的大型项目中,几乎都会在imports中导入许多的模块,而通过nest模块化的特性,使得nest相比于其他的框架更支持大型项目的扩展

回想一下,如果是express,可没有这样的模块化管理方式,不是吗?

main.ts

在任何程序中,mian.ts都是作为程序的入口文件,nest也不例外

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();

在这段代码中,首先定义了一个异步的bootstrap()函数,其是用来启动应用程序的

在函数内部,使用了NestFactory.create()方法创建了一个nest实例,并传入了AppModule,告诉nest如何去使用AppModule中定义的结构去构建应用程序

最后,监听了3000端口,所以可以在本地的3000端口访问nest

逻辑

nest中采取模块化的开发方式,在Module中定义了谁是控制器(路由) ,谁是提供者(路由处理程序) ,最后将Module作为参数传入入口文件 ,告诉nest依据这个模块的结构去生成对应的应用程序

本篇最后

码字不易,这一过程涉及到如何将晦涩的概念以通俗的言语表达出来,如果感觉这篇文章对您有帮助,笔者希望能得到您的评论+关注 !您的评论+关注是我更文的最大动力!

如果您发现有错字,还请见谅并给予指正建议,笔者会在最短时间内修改并私聊感谢

如果由于不可抗拒因素导致拖更,还请您见谅!

如果需进一步技术交流,请您在首页联系方式内联系我!

相关推荐
大鲤余27 分钟前
Rust,删除cargo安装的可执行文件
开发语言·后端·rust
她说彩礼65万35 分钟前
Asp.NET Core Mvc中一个视图怎么设置多个强数据类型
后端·asp.net·mvc
陈随易41 分钟前
农村程序员-关于小孩教育的思考
前端·后端·程序员
_江南一点雨44 分钟前
SpringBoot 3.3.5 试用CRaC,启动速度提升3到10倍
java·spring boot·后端
酸奶代码1 小时前
Spring AOP技术
java·后端·spring
代码小鑫1 小时前
A034-基于Spring Boot的供应商管理系统的设计与实现
java·开发语言·spring boot·后端·spring·毕业设计
paopaokaka_luck1 小时前
基于Spring Boot+Vue的多媒体素材管理系统的设计与实现
java·数据库·vue.js·spring boot·后端·算法
程序猿麦小七2 小时前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
蓝田~2 小时前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
theLuckyLong2 小时前
SpringBoot后端解决跨域问题
spring boot·后端·python