Home  >  Article  >  Web Front-end  >  A brief discussion on the automatic loading of back-end routes in node.js

A brief discussion on the automatic loading of back-end routes in node.js

青灯夜游
青灯夜游forward
2022-10-11 19:48:521508browse

A brief discussion on the automatic loading of back-end routes in node.js

This article is suitable for people

Node.js developers with a certain foundation

Difficulty level

Medium

Background

Today let’s talk about routing issues in the node backend . [Related tutorial recommendations: nodejs video tutorial]

Our front-end students or nodejs server students, when you use express and koajs to write interfaces, we don’t have to write routes, for example As follows

Login interfacerouter.post('/user/login', user.login);

Get user information interfacerouter.get('/ user/info', checkAuth, user.xxx);

This way of writing is very common. Register the route first, and then specify the middleware method to be executed later.

But when there are more and more interfaces, for example, 1,000 interfaces, you have to register like this 1,000 times. I think it is a very troublesome and inelegant thing if there are more interfaces.

koa&express routing registration example

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个

Does it require registering 1,000 times in router.js when writing 1,000 interfaces?

eggjs route registration example

'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);
};

**When this kind of project expands, I think this configuration will appear very redundant, so it is necessary to implement a route automatic loading mechanism. To improve it and optimize it.

1. Improve efficiency

2. Write more elegantly

Automatic loading of common routes

After contacting it, I found Several frameworks implement route autoloading in different ways.

1. think series

The first one is thinkPHP and thinkjs, reference linkthinkjs.org/zh-cn/doc /3…

The relationship between the two is that thinkjs was later designed and developed according to the ideas of thinkPHP.

The automatic loading of the other two routes is file-based, which means that after you write the controller name and method name, you can access the route directly without additional configuration.

1. Thinkphp’s routing is automatically loaded

tp is automatically loaded according to the module/controller/method file name

module?/controller/Action

For example, the following Admin module Next, the index method in AdlistController.class.php His route will be automatically loaded as Admin/adList/index

A brief discussion on the automatic loading of back-end routes in node.js

##2. Thinkjs’ route will be automatically loaded as

Controller file automatic loading logic

1), application initialization, create instance

....

2), traverse the controller directory, load the controller

Get the Map of the exported class corresponding to the directory file

For example, in the Controller directory He will load modules, controllers, and methods and hang them on his app.

A brief discussion on the automatic loading of back-end routes in node.js

{
  '/order': [class default_1 extends default_1],
  '/user': [class default_1 extends default_1]
}

3. Controller matching part

The previous step is done during the startup phase of the thinkjs application.

This step

The controller matching part is what is done when the request comes in.

That is, when a request comes in, it will pass through first. Think-router parses the module, controller, action and hangs it on ctx.

Here, take the module, controller, and action of this request on ctx and match them with the module, controller, action, and list hung in the app at startup. If there is one, execute it.

For details on the matching logic of think-controller, please see

github.com/thinkjs/thi…

The difference between thinkjs and koa-router route matching
1. After think think-router parses, think-controller performs matching. This is dynamic matching.

2. After koa-router matches the route, you can use koa-compose to assemble a small onion ring to execute
! My understanding is that the sequence is registered when the program starts
image.png

A brief discussion on the automatic loading of back-end routes in node.jsA brief discussion on the automatic loading of back-end routes in node.js

Summary: thinkjs first loads your controller and methods, and finally when the request comes in, use

think-controller Match the module/controller first, and then match the method. If there is one, it will be executed for you. If not, just 404

2. Take the egg modified version as an example of the decorator The route is automatically loaded

#The decorator is written similarly to the annotations in java spring

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>

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

get接口 就是 @Get()

post的接口 就是 @Post()

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

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

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>

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

总结

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

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

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

The above is the detailed content of A brief discussion on the automatic loading of back-end routes in node.js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete