从0~1手写Nest.js - (5) 配置路由

一. 前言

在前五篇文章中我们已经完成了以下搭建起Nest服务器几个必要准备工作

  • 一个极简的Nest服务器的搭建
  • 封装Logger日志类,在控制台打印项目运行日志
  • 自定义模块装饰器 / 控制器装饰器 / 参数装饰器
  • 配置路径别名
  • 实现项目热重载

二. 理解Nest中的路由系统

前期工作都准备完毕之后,这篇文章继续着手于下一个挑战,那就是写接口!

挑战1: app.controller.ts中存在一个hello接口

已知接口路径是/hello,请求方式是GET,返回值是hello nest

ts 复制代码
// 导入Controller和Get装饰器
import { Get, Controller } from '@nestjs/common'

// 使用@Controller装饰器标记类为控制器
@Controller()
export class AppController {
  // 使用@Get装饰器标记方法为处理GET请求的路由
  @Get('hello')
  hello(): string {
    return 'hello'
  }

  @Get('info')
  getInfo(): string {
    return 'info'
  }
}

如何在浏览器中输入http://localhost:4000/hello时,正确的获取到hello接口返回的hello nest数据呢?

现在试一下,直接在浏览器中访问hello接口会得到如下:

Cannot GET/hello ? 解释一下就是这通常意味着服务器无法找到请求的资源

拿前端页面来对比一下我觉得会更加的便于理解

拿Vue项目来说,当在浏览器中访问一个router.ts/js中不存在/或者说尚未的路由路径后, 通常会报404 notFound这个异常,这代表着所访问的路由路径并没在路由系统中注册过,所以路由系统不认识这个路径,从而会抛出异常

是不是和我们目前遇到的这个错误非常的相似? ~~ ~~ 因为我们无论是原生node 还是基于原生node所创建的Express Koa 还是基于Express的Nest所写的接口,也都是有路由系统的

比如在Nest@Controller装饰器就是设置统一的路由前缀而在@GET/@Post等参数装饰器中设置的是路由路径, 可以理解为vue-router中的pathchildrenPath的关系

三. 解决问题: 项目启动时,如何注册(映射)全部路由

nestApplication.ts

ts 复制代码
// 导入元数据包
import 'reflect-metadata'
import express from 'express'
import type {
  Express,
  Request as ExpressRequest,
  Response as ExpressResponse,
  NextFunction as ExpressNextFunction
} from 'express'
import { Logger } from './logger'
import path from 'path'

export class NestApplication {
  // 在内部私有化一个express实例
  private readonly app: Express = express()

  // protected readonly module等同于 this.module = module
  constructor(protected readonly module) {}

  // 初始化配置
  async init() {
    // 获取模块中所有的控制器类,准备做路由映射
    const controllers = Reflect.getMetadata('controllers', this.module)

    // 打印执行日志
    Logger.log(`${this.module.name} dependencies initialized`, 'InstanceLoader')

    // 路由映射的核心是要知道,什么样的请求方法,什么样的请求路径,请求是对应的那个处理函数
    for (const Controller of controllers) {
      // 创建每个控制器的实例
      const controllerInstance = new Controller()

      // 获取每个控制器的路径前缀
      const prefix = Reflect.getMetadata('prefix', Controller) || '/'

      // 打印执行日志: 提示开始路由解析
      Logger.log(`${Controller.name} {${prefix}}`, 'RoutesResolver')

      // 获取每个控制器类的原型
      const controllerPrototype = Controller.prototype

      // 遍历控制器类原型上的方法名
      for (const methodName of Object.getOwnPropertyNames(controllerPrototype)) {
        // 根据方法名获取该控制器类原型上的方法
        const method = controllerPrototype[methodName]

        // 获取该方法上的元数据
        const httpMethod = Reflect.getMetadata('method', method)
        const pathMetadata = Reflect.getMetadata('path', method)

        // 如果方法名不存在(没有写GET/POST...请求装饰器的),则不处理
        if (!httpMethod) continue

        // 拼出完整的路由路径
        const routePath = path.posix.join('/', prefix, pathMetadata)

        // 配置路由,当客户端以httpMethod方法请求routePath路径的时候,会由对应的函数进行处理
        this.app[httpMethod.toLowerCase()](
          routePath,
          (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => {
            const result = method.call(controllerInstance)
            res.send(result)
          }
        )
        Logger.log(`Mapped {${routePath}, ${httpMethod}} route`, 'RoutesResolver')
      }
    }
  }

  // 启动HTTP服务器
  async listen(port: number) {
    this.init()

    // 调用express实例的listen方法,启动一个HTTP服务器,监听port端口
    this.app.listen(port, () => {
      Logger.log(`Application is running on http://localhost:${port}`, 'NestApplication')
    })
  }
}

总结

  • init() 方法扫描模块中的所有控制器
  • 遍历控制器中的方法,检查每个方法的 HTTP 请求装饰器(如 @Get@Post
  • 基于装饰器信息注册路由,建立 HTTP 路由与控制器方法的映射
相关推荐
YanceyOfficial9 天前
How to deploy Nest.js microservices using Kubernetes
微服务·kubernetes·nestjs
webxue16 天前
NestJS配置环境变量、读取Yaml配置的保姆级教程
node.js·nestjs
超级无敌暴龙兽19 天前
微服务架构的基础与实践:构建灵活的分布式系统
微服务·node.js·nestjs
寻找奶酪的mouse21 天前
【NestJS全栈之旅】应用篇:通用爬虫服务三两事儿
前端·后端·nestjs
_jiang23 天前
nestjs 入门实战最强篇
redis·typescript·nestjs
敲代码的彭于晏1 个月前
【Nest.js 10】JWT+Redis实现登录互踢
前端·后端·nestjs
前端小王hs1 个月前
Nest通用工具函数执行顺序
javascript·后端·nestjs
明远湖之鱼1 个月前
从入门到入门学习NestJS
前端·后端·nestjs
吃葡萄不吐番茄皮1 个月前
从零开始学 NestJS(一):为什么要学习 Nest
前端·nestjs