从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 路由与控制器方法的映射
相关推荐
亮子AI6 天前
【NestJS】为什么return不返回客户端?
前端·javascript·git·nestjs
小p6 天前
nestjs学习2:利用typescript改写express服务
nestjs
Eric_见嘉12 天前
NestJS 🧑‍🍳 厨子必修课(九):API 文档 Swagger
前端·后端·nestjs
XiaoYu200220 天前
第3章 Nest.js拦截器
前端·ai编程·nestjs
XiaoYu200222 天前
第2章 Nest.js入门
前端·ai编程·nestjs
实习生小黄22 天前
NestJS 调试方案
后端·nestjs
当时只道寻常25 天前
NestJS 如何配置环境变量
nestjs
濮水大叔1 个月前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
ovensi1 个月前
告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南
nestjs