欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長(zhǎng)資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          一文聊聊node后端路由自動(dòng)加載

          一文聊聊node后端路由自動(dòng)加載

          本文適宜人群

          有一定基礎(chǔ)的Node.js開(kāi)發(fā)人員

          難易程度

          中等

          背景

          今天來(lái)談?wù)刵ode后端中路由的問(wèn)題?!鞠嚓P(guān)教程推薦:nodejs視頻教程】

          我們前端同學(xué)或者是nodejs服務(wù)端的同學(xué),在你們使用express和koajs寫接口的時(shí)候, 咱們是不都要寫路由 比如如下

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

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

          這種寫法很常見(jiàn), 先注冊(cè)路由,再指定后續(xù)要執(zhí)行的中間件方法。

          可是當(dāng)接口越來(lái)越多,比如有1000個(gè)接口, 就得這樣注冊(cè)1000次,多了我認(rèn)為是一件很麻煩且不優(yōu)雅的事情

          koa&express 路由注冊(cè)示例

          const express = require('express'); const router = express.Router(); const user = require('../../controllers/user'); const tokenCheck = require('../../middleware/token_check_api');  //用戶注冊(cè) router.post('/user/register', user.register); //用戶登錄 router.post('/user/login', user.login); router.post('xxx', tokenCheck, user.xxx); ...假裝還有有1000個(gè)
          登錄后復(fù)制

          寫1000個(gè)接口就要在router.js里注冊(cè)1000次嗎?

          eggjs路由注冊(cè)示例

          '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); };
          登錄后復(fù)制

          **這種項(xiàng)目擴(kuò)大時(shí)候, 我認(rèn)為這種配置會(huì)顯得很冗余,所以就需要實(shí)現(xiàn)一種路由自動(dòng)加載的機(jī)制來(lái)改善它優(yōu)化它。

          1、提升效率

          2、更優(yōu)雅的編寫

          常見(jiàn)的路由自動(dòng)加載

          接觸下來(lái), 我發(fā)現(xiàn)有幾個(gè)框架用不同方法實(shí)現(xiàn)了路由自動(dòng)加載。

          一、think系列

          第一個(gè)是thinkPHP和thinkjs, 參考鏈接 thinkjs.org/zh-cn/doc/3…

          他兩的關(guān)系屬于是thinkjs是后來(lái)按照thinkPHP的思想設(shè)計(jì)開(kāi)發(fā)的。

          他兩路由自動(dòng)加載屬于基于文件的, 就是說(shuō)你按控制器的名字和方法名寫好,直接就可以訪問(wèn)路由,不需要額外的配置。

          1、thinkphp的路由自動(dòng)加載

          tp是 按模塊/控制器/方法文件名 自動(dòng)加載

          module?/controller/Action
          登錄后復(fù)制

          比方下面這個(gè)Admin模塊下, AdlistController.class.php里 index方法 他的路由就會(huì)自動(dòng)加載為 Admin/adList/index

          一文聊聊node后端路由自動(dòng)加載

          2、thinkjs的路由自動(dòng)加載

          控制器文件文件自動(dòng)加載邏輯

          1)、應(yīng)用初始化,創(chuàng)建實(shí)例
          ….

          2)、遍歷控制器目錄, 加載控制器

          得到目錄文件對(duì)應(yīng)的導(dǎo)出class的 Map
          例如 Controller目錄下 他會(huì)加載出來(lái)模塊、控制器、方法掛在他的app上。

          一文聊聊node后端路由自動(dòng)加載

          {   '/order': [class default_1 extends default_1],   '/user': [class default_1 extends default_1] }
          登錄后復(fù)制

          3、控制器匹配部分

          上一步是在thinkjs應(yīng)用啟動(dòng)階段做的事情。

          這一步 控制器匹配部分 是在當(dāng)請(qǐng)求進(jìn)來(lái)的時(shí)候做的事情。

          就是當(dāng)請(qǐng)求進(jìn)來(lái),會(huì)先進(jìn)過(guò),think-router 把module, controller, action ,解析出來(lái)掛在ctx上。

          在這里拿ctx上本次請(qǐng)求的module, controller, action去和啟動(dòng)時(shí)掛在app的 module, controller, action,列表去匹配, 如果有就執(zhí)行。

          think-controller的匹配邏輯詳見(jiàn) github.com/thinkjs/thi…

          thinkjs和koa-router路由匹配的區(qū)別

          1、 think think-router解析完, think-controller去匹配執(zhí)行, 他這個(gè)是動(dòng)態(tài)匹配。
          2、koa-router 匹配到路由后, 自己再用koa-compose組裝一個(gè)小洋蔥圈去執(zhí)行
          ! 這種我的理解是程序啟動(dòng)就注冊(cè)好的順序image.png

          一文聊聊node后端路由自動(dòng)加載

          一文聊聊node后端路由自動(dòng)加載

          總結(jié):thinkjs是先把你的控制器和方法加載出來(lái), 最后當(dāng)請(qǐng)求進(jìn)來(lái)的時(shí)候,利用think-controller 去先匹配模塊/控制器,再匹配方法, 如果有的話就幫你執(zhí)行,沒(méi)有的話,就404

          二、以egg改造版為例 裝飾器的路由自動(dòng)加載

          裝飾器的寫法類似于 java spring中的注解

          node框架中 nestjsmidwayjs已經(jīng)全面擁抱了裝飾器路由。

          • 寫法比較優(yōu)雅
          • 建議控制器的文件名和控制器名字保持一致, 這樣你找api也比較好找 比如控制的文件名字叫 home.ts , 那你控制器注冊(cè)也寫 @controller('/home') 來(lái)保持一致。

          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> {}    }
          登錄后復(fù)制

          2、方法裝飾器 @Get('/export')、 @Post('/list')

          get接口 就是 @Get()

          post的接口 就是 @Post()

            @Get("/")   public async index(): Promise<void> {}    @Post("/update")   public async update(): Promise<void> {}
          登錄后復(fù)制

          3、裝飾器路由統(tǒng)一注冊(cè)

          這里統(tǒng)一按egg的方法循環(huán)注冊(cè)路由

          '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 裝飾器,設(shè)置api公共前綴  * @param pathPrefix {string}  * @constructor  */ export const SelfController = (pathPrefix?: string): ClassDecorator => (targetClass): void => {   // 在controller上定義pathPrefix的元數(shù)據(jù)   // https://github.com/rbuckton/reflect-metadata    (Reflect as any).defineMetadata(CONTROLLER_PREFIX, pathPrefix, targetClass); };  const methodWrap = (path: string, requestMethod: string): MethodDecorator => (target, methodName): void => {   // 路由裝飾器參數(shù)為空時(shí),路由為方法名   const key = path ? `${requestMethod}·${path}·${String(methodName)}` : `${requestMethod}·${String(methodName)}·/${String(methodName)}`;   methodMap.set(key, target); };  // Post 請(qǐng)求 export const Post = (path: string = ''): MethodDecorator => methodWrap(path, 'post');  // Get 請(qǐng)求 export const Get = (path: string = ''): MethodDecorator => methodWrap(path, 'get');  export default (app: Application): void => {   const { router } = app;   // 遍歷methodMap, 注冊(cè)路由   methodMap.forEach((curController: CurController, configString: string) => {     // 請(qǐng)求方法, 請(qǐng)求路徑, 方法名      const [ requestMethod, path, methodName ] = configString.split(`·`);     // 獲取controller裝飾器設(shè)置的公共前綴     // 如果controller沒(méi)有添加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);     };     // 注冊(cè)路由     router[requestMethod](rootApiPath + controllerPrefix + path, func);   }); };
          登錄后復(fù)制

          建議使用node寫服務(wù)直接上midwayjs或者nestjs

          總結(jié)

          通過(guò)如上比較,相信你對(duì)think系列框架堵文件的路由自動(dòng)加載和裝飾器的路由加載,有了一定了解, 他們的這種設(shè)計(jì)思想值得學(xué)習(xí)吧, 希望對(duì)你有所啟發(fā)。

          還有我認(rèn)為裝飾器的路由寫起來(lái),比較優(yōu)雅, 不知道各位小伙伴怎么看,評(píng)論區(qū)說(shuō)說(shuō)?

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)