Heim > Web-Frontend > js-Tutorial > Hauptteil

Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js

青灯夜游
Freigeben: 2022-10-11 19:48:52
nach vorne
1481 Leute haben es durchsucht

Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js

Dieser Artikel ist für Personen geeignet

Node.js-Entwickler mit einer bestimmten Grundlage有一定基础的Node.js开发人员

难易程度

中等

背景

今天来谈谈node后端中路由的问题。【相关教程推荐:nodejs视频教程

我们前端同学或者是nodejs服务端的同学,在你们使用express和koajs写接口的时候, 咱们是不都要写路由 比如如下

登录接口router.post('/user/login', user.login);

获取用户信息接口router.get('/user/info', checkAuth, user.xxx);

这种写法很常见, 先注册路由,再指定后续要执行的中间件方法。

可是当接口越来越多,比如有1000个接口, 就得这样注册1000次,多了我认为是一件很麻烦且不优雅的事情

koa&express 路由注册示例

const express = require('express');
const router = express.Router();
const user = require('../../controllers/user');
const tokenCheck = require('../../middleware/token_check_api');

//用户注册
router.post('/user/register', user.register);
//用户登录
router.post('/user/login', user.login);
router.post('xxx', tokenCheck, user.xxx);
...假装还有有1000个
Nach dem Login kopieren

写1000个接口就要在router.js里注册1000次吗?

eggjs路由注册示例

'use strict';

// egg-router extends koa-router

import { Application } from 'egg';

export default (app: Application) => {
  const { router, controller, middleware } = app;
  router.get('/', middleware.special(), controller.home.index);
  router.get('/1', middleware.special(), controller.home.index1);
  ....
  router.get('/error', controller.home.error);
};
Nach dem Login kopieren

**这种项目扩大时候, 我认为这种配置会显得很冗余,所以就需要实现一种路由自动加载的机制来改善它优化它。

1、提升效率

2、更优雅的编写

常见的路由自动加载

接触下来, 我发现有几个框架用不同方法实现了路由自动加载。

一、think系列

第一个是thinkPHP和thinkjs, 参考链接 thinkjs.org/zh-cn/doc/3…

他两的关系属于是thinkjs是后来按照thinkPHP的思想设计开发的。

他两路由自动加载属于基于文件的, 就是说你按控制器的名字和方法名写好,直接就可以访问路由,不需要额外的配置。

1、thinkphp的路由自动加载

tp是 按模块/控制器/方法文件名 自动加载

module?/controller/Action
Nach dem Login kopieren

比方下面这个Admin模块下, AdlistController.class.php里 index方法 他的路由就会自动加载为 Admin/adList/index

Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js

2、thinkjs的路由自动加载

控制器文件文件自动加载逻辑

1)、应用初始化,创建实例
....

2)、遍历控制器目录, 加载控制器

得到目录文件对应的导出class的 Map
例如 Controller目录下 他会加载出来模块、控制器、方法挂在他的app上。

Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js

{
  '/order': [class default_1 extends default_1],
  '/user': [class default_1 extends default_1]
}
Nach dem Login kopieren

3、控制器匹配部分

上一步是在thinkjs应用启动阶段做的事情。

这一步 控制器匹配部分 是在当请求进来的时候做的事情。

就是当请求进来,会先进过,think-router 把module, controller, action ,解析出来挂在ctx上。

在这里拿ctx上本次请求的module, controller, action去和启动时挂在app的 module, controller, action,列表去匹配, 如果有就执行。

think-controller的匹配逻辑详见 github.com/thinkjs/thi…

thinkjs和koa-router路由匹配的区别

1、 think  think-router解析完, think-controller去匹配执行, 他这个是动态匹配。
2、koa-router 匹配到路由后, 自己再用koa-compose组装一个小洋葱圈去执行
! 这种我的理解是程序启动就注册好的顺序image.png

Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.jsEine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js

总结:thinkjs是先把你的控制器和方法加载出来, 最后当请求进来的时候,利用think-controller

Schwierigkeitsgrad

Mittel

Hintergrund

Lass uns heute über Routing-Probleme im Node-Backend sprechen. [Empfohlene verwandte Tutorials: nodejs-Video-Tutorial]Unsere Front-End-Klassenkameraden bzw nodejs Serverseitige Studenten: Wenn Sie Express und Koajs zum Schreiben von Schnittstellen verwenden, müssen wir keine Routen schreiben, zum Beispiel wie folgt

🎜Anmeldeschnittstelle router.post('/user/login', user.login );🎜🎜Benutzerinformationsschnittstelle abrufenrouter.get('/user/info', checkAuth, user.xxx);🎜🎜Diese Schreibmethode ist sehr verbreitet. Registrieren Sie zuerst die Route und geben Sie dann die nachfolgenden Middleware-Methoden an. 🎜🎜🎜Aber wenn es immer mehr Schnittstellen gibt, zum Beispiel 1000 Schnittstellen, muss man sich 1000 Mal auf diese Weise registrieren. Wenn es mehr Schnittstellen gibt, ist das meiner Meinung nach eine sehr mühsame und unelegante Sache 🎜🎜🎜Koa&Express-Routing-Registrierungsbeispiel 🎜
'use strict';

import { Context } from 'egg';
import BaseController from './base';
import { formatDate } from '~/app/lib/utils';
import { SelfController, Get } from './../router'

@SelfController('/home')
export default class HomeController extends BaseController {
  [x: string]: any;
  @validate()
  @Get("/")
  public async index(): Promise<void> {}
  
}</void>
Nach dem Login kopieren
Nach dem Login kopieren
🎜🎜write Müssen 1.000 Schnittstellen 1.000 Mal in router.js registriert werden? 🎜🎜🎜Eggjs-Routenregistrierungsbeispiel🎜
  @Get("/")
  public async index(): Promise<void> {}

  @Post("/update")
  public async update(): Promise<void> {}</void></void>
Nach dem Login kopieren
Nach dem Login kopieren
🎜**Wenn diese Art von Projekt erweitert wird, denke ich, dass diese Konfiguration sehr überflüssig wird, sodass es notwendig ist, einen automatischen Routenlademechanismus zu implementieren, um sie zu verbessern und zu optimieren. 1. Effizienz verbessern 🎜🎜🎜🎜1. Think-Serie 🎜🎜🎜🎜Die erste ist thinkPHP und thinkjs, Referenzlink thinkjs.org/zh-cn/doc/3…🎜🎜Die Beziehung zwischen den beiden gehört zu thinkjs und später Es wurde nach dem Denken von thinkPHP entworfen und entwickelt. 🎜🎜Das automatische Laden der beiden anderen Routen erfolgt dateibasiert, was bedeutet, dass Sie nach Eingabe des Controller-Namens und Methodennamens direkt und ohne zusätzliche Konfiguration auf die Route zugreifen können. 🎜🎜🎜1. Die Route von thinkphp wird automatisch geladen🎜🎜🎜tp wird automatisch entsprechend dem Modul-/Controller-/Methodendateinamen geladen🎜
'use strict';

import { Application, Context } from 'egg';
import 'reflect-metadata';

const CONTROLLER_PREFIX: string = '';
const methodMap: Map<string> = new Map<string>();
const rootApiPath: string = '';

interface CurController {
  pathName: string;
  fullPath: string;
}

/**
 * controller 装饰器,设置api公共前缀
 * @param pathPrefix {string}
 * @constructor
 */
export const SelfController = (pathPrefix?: string): ClassDecorator => (targetClass): void => {
  // 在controller上定义pathPrefix的元数据
  // https://github.com/rbuckton/reflect-metadata

  (Reflect as any).defineMetadata(CONTROLLER_PREFIX, pathPrefix, targetClass);
};

const methodWrap = (path: string, requestMethod: string): MethodDecorator => (target, methodName): void => {
  // 路由装饰器参数为空时,路由为方法名
  const key = path ? `${requestMethod}·${path}·${String(methodName)}` : `${requestMethod}·${String(methodName)}·/${String(methodName)}`;
  methodMap.set(key, target);
};

// Post 请求
export const Post = (path: string = ''): MethodDecorator => methodWrap(path, 'post');

// Get 请求
export const Get = (path: string = ''): MethodDecorator => methodWrap(path, 'get');

export default (app: Application): void => {
  const { router } = app;
  // 遍历methodMap, 注册路由
  methodMap.forEach((curController: CurController, configString: string) => {
    // 请求方法, 请求路径, 方法名 
    const [ requestMethod, path, methodName ] = configString.split(`·`);
    // 获取controller装饰器设置的公共前缀
    // 如果controller没有添加SelfController装饰器,则取文件名作为路径
    let controllerPrefix: string | undefined | null = (Reflect as any).getMetadata(CONTROLLER_PREFIX, curController.constructor);
    if (!(Reflect as any).hasMetadata(CONTROLLER_PREFIX, curController.constructor)) {
      controllerPrefix = `/${curController.pathName.split(`.`).reverse()[0]}`;
    }
    const func: (this: Context, ...args: any[]) => Promise<any> = async function (...args: any[]): Promise<any> {
      return new (curController.constructor as any)(this)[methodName](...args);
    };
    // 注册路由
    router[requestMethod](rootApiPath + controllerPrefix + path, func);
  });
};</any></any></string></string>
Nach dem Login kopieren
Nach dem Login kopieren
🎜Zum Beispiel unter dem Admin-Modul unten die Indexmethode in AdlistController.class.php Seine Route wird automatisch als Admin/adList/index🎜🎜Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js🎜🎜🎜2. Automatisches Laden von ThinkJS-Routen🎜🎜🎜Logik zum automatischen Laden von Controller-Dateien🎜🎜1), Anwendungsinitialisierung, Instanzerstellung
. ..🎜🎜2), durchqueren Sie das Controller-Verzeichnis, laden Sie den Controller🎜🎜 und rufen Sie die Map der exportierten Klasse ab, die der Verzeichnisdatei entspricht
Zum Beispiel im Controller-Verzeichnis Er lädt Module, Controller und Methoden und hängt sie an seine App. 🎜🎜Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js🎜rrreee🎜 🎜3. Controller-Matching-Teil🎜🎜🎜Der vorherige Schritt wird während der Startphase der thinkjs-Anwendung durchgeführt. 🎜🎜Dieser Schritt Controller-Matching-Teil ist abgeschlossen, wenn die Anfrage eingeht. 🎜🎜Das heißt, wenn eine Anfrage eingeht, wird sie zuerst durch den Think-Router geleitet, analysiert das Modul, den Controller und die Aktion und hängt sie an ctx. 🎜🎜Nehmen Sie hier das Modul, den Controller und die Aktion dieser Anfrage auf ctx und ordnen Sie sie dem Modul, dem Controller, der Aktion und der Liste zu, die beim Start in der App hängen. Falls vorhanden, führen Sie sie aus. 🎜🎜Die Matching-Logik von Think-Controller wird in github.com/thinkjs/thi…🎜
🎜Der Unterschied zwischen thinkjs und koa-router Route Matching🎜
🎜1. think Nach der -router-Analyse führt think-controller einen Abgleich durch. Dies ist ein dynamischer Abgleich.
2. Nachdem koa-router mit der Route übereinstimmt, können Sie mit koa-compose einen kleinen Zwiebelring zur Ausführung zusammenstellen
! https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/17afb9d562bd48b4ae5a6e97dd886fe8~tplv-k3u1fbpfcp-zoom-1.image" target="_blank" textvalue="image.png">image.png a>🎜🎜Eine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js🎜🎜Zusammenfassung: thinkjs ist Laden Sie zunächst Ihren Controller und Ihre Methoden. Wenn die Anfrage eingeht, verwenden Sie think-controller, um zuerst das Modul/den Controller abzugleichen und dann die Methode auszuführen. Wenn nicht, nur 404

node框架中 nestjsmidwayjs已经全面拥抱了装饰器路由。

  • 写法比较优雅
  • 建议控制器的文件名和控制器名字保持一致, 这样你找api也比较好找 比如控制的文件名字叫 home.ts , 那你控制器注册也写 @controller('/home') 来保持一致。

1、 控制器装饰器 @controller('/order')

'use strict';

import { Context } from 'egg';
import BaseController from './base';
import { formatDate } from '~/app/lib/utils';
import { SelfController, Get } from './../router'

@SelfController('/home')
export default class HomeController extends BaseController {
  [x: string]: any;
  @validate()
  @Get("/")
  public async index(): Promise<void> {}
  
}</void>
Nach dem Login kopieren
Nach dem Login kopieren

2、方法装饰器 @Get('/export')、 @Post('/list')

get接口 就是 @Get()

post的接口 就是 @Post()

  @Get("/")
  public async index(): Promise<void> {}

  @Post("/update")
  public async update(): Promise<void> {}</void></void>
Nach dem Login kopieren
Nach dem Login kopieren

3、装饰器路由统一注册

这里统一按egg的方法循环注册路由

'use strict';

import { Application, Context } from 'egg';
import 'reflect-metadata';

const CONTROLLER_PREFIX: string = '';
const methodMap: Map<string> = new Map<string>();
const rootApiPath: string = '';

interface CurController {
  pathName: string;
  fullPath: string;
}

/**
 * controller 装饰器,设置api公共前缀
 * @param pathPrefix {string}
 * @constructor
 */
export const SelfController = (pathPrefix?: string): ClassDecorator => (targetClass): void => {
  // 在controller上定义pathPrefix的元数据
  // https://github.com/rbuckton/reflect-metadata

  (Reflect as any).defineMetadata(CONTROLLER_PREFIX, pathPrefix, targetClass);
};

const methodWrap = (path: string, requestMethod: string): MethodDecorator => (target, methodName): void => {
  // 路由装饰器参数为空时,路由为方法名
  const key = path ? `${requestMethod}·${path}·${String(methodName)}` : `${requestMethod}·${String(methodName)}·/${String(methodName)}`;
  methodMap.set(key, target);
};

// Post 请求
export const Post = (path: string = ''): MethodDecorator => methodWrap(path, 'post');

// Get 请求
export const Get = (path: string = ''): MethodDecorator => methodWrap(path, 'get');

export default (app: Application): void => {
  const { router } = app;
  // 遍历methodMap, 注册路由
  methodMap.forEach((curController: CurController, configString: string) => {
    // 请求方法, 请求路径, 方法名 
    const [ requestMethod, path, methodName ] = configString.split(`·`);
    // 获取controller装饰器设置的公共前缀
    // 如果controller没有添加SelfController装饰器,则取文件名作为路径
    let controllerPrefix: string | undefined | null = (Reflect as any).getMetadata(CONTROLLER_PREFIX, curController.constructor);
    if (!(Reflect as any).hasMetadata(CONTROLLER_PREFIX, curController.constructor)) {
      controllerPrefix = `/${curController.pathName.split(`.`).reverse()[0]}`;
    }
    const func: (this: Context, ...args: any[]) => Promise<any> = async function (...args: any[]): Promise<any> {
      return new (curController.constructor as any)(this)[methodName](...args);
    };
    // 注册路由
    router[requestMethod](rootApiPath + controllerPrefix + path, func);
  });
};</any></any></string></string>
Nach dem Login kopieren
Nach dem Login kopieren

建议使用node写服务直接上midwayjs或者nestjs

总结

通过如上比较,相信你对think系列框架堵文件的路由自动加载和装饰器的路由加载,有了一定了解, 他们的这种设计思想值得学习吧, 希望对你有所启发。

还有我认为装饰器的路由写起来,比较优雅, 不知道各位小伙伴怎么看,评论区说说?

更多node相关知识,请访问:nodejs 教程

Das obige ist der detaillierte Inhalt vonEine kurze Diskussion zum automatischen Laden von Back-End-Routen in node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!