Home > Web Front-end > JS Tutorial > An article to talk about the automatic loading of node back-end routing

An article to talk about the automatic loading of node back-end routing

青灯夜游
Release: 2023-02-06 20:14:31
forward
1629 people have browsed it

An article to talk about the automatic loading of node back-end routing

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个
Copy after login

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);
};
Copy after login

**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
Copy after login

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

An article to talk about the automatic loading of node back-end routing

##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.

An article to talk about the automatic loading of node back-end routing

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

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 of 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

An article to talk about the automatic loading of node back-end routing

An article to talk about the automatic loading of node back-end routing

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

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

2. Use egg The modified version takes the decorator route as an example to automatically load

The writing method of the decorator is similar to the annotations in java spring

in the node framework

nestjs and midwayjs have fully embraced decorator routing.

  • 写法比较优雅
  • 建议控制器的文件名和控制器名字保持一致, 这样你找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> {}
  
}
Copy after login

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

get接口 就是 @Get()

post的接口 就是 @Post()

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

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

3、装饰器路由统一注册

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

'use strict';

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

const CONTROLLER_PREFIX: string = '';
const methodMap: Map<string, any> = new Map<string, any>();
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);
  });
};
Copy after login

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

总结

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

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

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

The above is the detailed content of An article to talk about the automatic loading of node back-end routing. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:juejin.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template