node.js極速入門課程:進入學習
有一定基礎的Node.js開發人員
中等
今天來談談node後端中路由的問題。【相關教學推薦:】
我們前端同學或者是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個
登入後複製
寫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);
};
登入後複製
**這種專案擴大時候, 我認為這種設定會顯得很冗餘,所以就需要實現一種路由自動載入的機制來改善它優化它。
1、提升效率
2、更優雅的編寫
接觸下來, 我發現有幾個框架用不同方法實現了路由自動載入。
一、think系列
第一個是thinkPHP和thinkjs, 參考連結 thinkjs.org/zh-cn/doc/3…
他兩的關係屬於是thinkjs是後來按照thinkPHP的思想設計開發的。
他兩路由自動載入屬於基於檔案的, 就是說你按控制器的名字和方法名寫好,直接就可以存取路由,不需要額外的設定。
1、thinkphp的路由自動載入
tp是 按模組/控制器/方法檔名 自動載入
module?/controller/Action
登入後複製
比方下面這個Admin模組下, AdlistController.class.php裡 index方法
他的路由就會自動載入為 Admin/adList/index
2、thinkjs的路由自動載入
控制器檔案檔案自動載入邏輯
1)、應用初始化,建立範例
....
2)、遍歷控制器目錄, 載入控制器
得到目錄檔案對應的匯出class的 Map
例如 Controller目錄下
他會載入出來模組、控制器、方法掛在他的app上。
{
'/order': [class default_1 extends default_1],
'/user': [class default_1 extends default_1]
}
登入後複製
3、控制器匹配部分
上一步是在thinkjs應用啟動階段做的事情。
這一步 控制器匹配部分
是在當請求進來的時候做的事情。
就是當請求進來,會先進過,think-router 把module, controller, action ,解析出來掛在ctx上。
在這裡拿ctx上本次請求的module, controller, action去和啟動時掛在app的 module, controller, action,列表去匹配, 如果有就執行。
think-controller的匹配邏輯詳見 github.com/thinkjs/thi…
1、 think think-router解析完, think-controller去匹配執行, 他這個是動態匹配。
2、koa-router 匹配到路由後, 自己再用koa-compose組裝一個小洋蔥圈去執行
! 這種我的理解是程式啟動就註冊好的順序image.png
總結:thinkjs是先把你的控制器和方法載入出來, 最後當請求進來的時候,利用think-controller
去先匹配模組/控制器,再匹配方法, 如果有的話就幫你執行,沒有的話,就404
二、以egg改造版為例 裝飾器的路由自動載入
裝飾器的寫法類似於 java spring中的註解
node框架中 nestjs
和midwayjs
已經全面擁抱了裝飾器路由。
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> {}
}
登入後複製
2、方法裝飾器 @Get('/export')、 @Post('/list')
get介面 就是 @Get()
post的介面 就是 @Post()
@Get("/")
public async index(): Promise<void> {}
@Post("/update")
public async update(): Promise<void> {}
登入後複製
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);
});
};
登入後複製
建議使用node寫服務直接上midwayjs或者nestjs
通過如上比較,相信你對think系列框架堵檔案的路由自動載入和裝飾器的路由載入,有了一定了解, 他們的這種設計思想值得學習吧
, 希望對你有所啟發。
還有我認為裝飾器的路由寫起來,比較優雅, 不知道各位小夥伴怎麼看,評論區說說?
更多node相關知識,請存取:!
以上就是淺談node.js的後端路由自動載入的詳細內容,更多請關注TW511.COM其它相關文章!