日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

【开源】NodeJS仿WebApi路由

發(fā)布時間:2024/8/26 javascript 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【开源】NodeJS仿WebApi路由 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

用過WebApi或Asp.net MVC的都知道微軟的路由設計得非常好,十分方便,也十分靈活。雖然個人看來是有的太靈活了,team內(nèi)的不同開發(fā)很容易使用不同的路由方式而顯得有點混亂。 不過這不是重點,我在做Node項目的時候就覺得不停的用use(...)來指定路由路徑很煩人,所以用Typescript寫了這個基于Koa和Koa-router的路由插件,可以簡單實現(xiàn)一些類似WebApi的路由功能。

目標是和WebApi一樣:

  • 加入的controller會自動加入路由。
  • 也可以通過path()手動指定路由。
  • 可以定義http method, 如GET或POST等。
  • Api的參數(shù)可以指定url里的query param、path param以及body等。
  • 包已經(jīng)上傳到npm中,npm install webapi-router 安裝,可以先看看效果:

    第一步,先設置controllers的目錄和url的固定前綴

    所有的controller都在這目錄下,這樣會根據(jù)物理路徑自動算出路由。 url的固定前綴就是host和路由之間的,比如localhost/api/v2/user/name,api/v2就是這個固定前綴。

    import { WebApiRouter } from 'webapi-router';app.use(new WebApiRouter().router('sample/controllers', 'api'));

    第二步是controller都繼承自BaseController

    export class TestController extends BaseController {}

    第三步給controller的方法加上裝飾器

    @POST('/user/:name') postWithPathParam(@PathParam('name') name: string, @QueryParam('id') id: string, @BodyParam body: any) {console.info(`TestController - post with name: ${name}, body: ${JSON.stringify(body)}`);return 'ok'; }

    @POST里的參數(shù)是可選的,空的話會用這個controller的物理路徑做為路由地址。

    :name是路徑里的變量,比如 /user/brook, :name就是brook,可以在方法的參數(shù)里用@PathParam得到

    @QueryParam可以得到url里?后的參數(shù)

    @BodyParam可以得到Post上來的body

    是不是有點WebApi的意思了。

    現(xiàn)在具體看看是怎么實現(xiàn)的

    實現(xiàn)過程其實很簡單,從上面的目標入手,首先得到controllers的物理路徑,然后還要得到被裝飾器裝飾的方法以及它的參數(shù)。
    裝飾器的目的在于要得到是Get還是Post等,還有就是指定的Path,最后就是把node request里的數(shù)據(jù)賦值給方法的參數(shù)。

    核心代碼:

    得到物理路徑

    initRouterForControllers() {//找出指定目錄下的所有繼承自BaseController的.js文件let files = FileUtil.getFiles(this.controllerFolder);files.forEach(file => {let exportClass = require(file).default;if(this.isAvalidController(exportClass)){this.setRouterForClass(exportClass, file);}}); }

    從物理路徑轉(zhuǎn)成路由

    private buildControllerRouter(file: string){let relativeFile = Path.relative(Path.join(FileUtil.getApiDir(), this.controllerFolder), file);let controllerPath = '/' + relativeFile.replace(/\\/g, '/').replace('.js','').toLowerCase();if(controllerPath.endsWith('controller'))controllerPath = controllerPath.substring(0, controllerPath.length - 10);return controllerPath; }

    裝飾器的實現(xiàn)

    裝飾器需要引入reflect-metadata庫

    先看看方法的裝飾器,@GET,@POST之類的,實現(xiàn)方法是給裝飾的方法加一個屬性Router,Router是個Symbol,確保唯一。 然后分析裝飾的功能存到這個屬性中,比如Method,Path等。

    export function GET(path?: string) {return (target: BaseController, name: string) => setMethodDecorator(target, name, 'GET', path); } function setMethodDecorator(target: BaseController, name: string, method: string, path?: string){target[Router] = target[Router] || {};target[Router][name] = target[Router][name] || {};target[Router][name].method = method;target[Router][name].path = path; }

    另外還有參數(shù)裝飾器,用來給參數(shù)賦上request里的值,如body,param等。

    export function BodyParam(target: BaseController, name: string, index: number) {setParamDecorator(target, name, index, { name: "", type: ParamType.Body }); }function setParamDecorator(target: BaseController, name: string, index: number, value: {name: string, type: ParamType}) {let paramTypes = Reflect.getMetadata("design:paramtypes", target, name);target[Router] = target[Router] || {};target[Router][name] = target[Router][name] || {};target[Router][name].params = target[Router][name].params || [];target[Router][name].params[index] = { type: paramTypes[index], name: value.name, paramType: value.type }; }

    這樣裝飾的數(shù)據(jù)就存到對象的Router屬性上,后面構建路由時就可以用了。

    綁定路由到Koa-router上

    上面從物理路徑得到了路由,但是是以裝飾里的參數(shù)路徑優(yōu)先,所以先看看剛在存在原型里的Router屬性里有沒有Path,有的話就用這個作為路由,沒有Path就用物理路由。

    private setRouterForClass(exportClass: any, file: string) { let controllerRouterPath = this.buildControllerRouter(file);let controller = new exportClass();for(let funcName in exportClass.prototype[Router]){let method = exportClass.prototype[Router][funcName].method.toLowerCase();let path = exportClass.prototype[Router][funcName].path;this.setRouterForFunction(method, controller, funcName, path ? `/${this.urlPrefix}${path}` : `/${this.urlPrefix}${controllerRouterPath}/${funcName}`);} }

    給controller里的方法參數(shù)賦上值并綁定路由到KoaRouter

    private setRouterForFunction(method: string, controller: any, funcName: string, routerPath: string){this.koaRouter[method](routerPath, async (ctx, next) => { await this.execApi(ctx, next, controller, funcName) }); }private async execApi(ctx: Koa.Context, next: Function, controller: any, funcName: string) : Promise<void> { //這里就是執(zhí)行controller的api方法了try{ctx.body = await controller[funcName](...this.buildFuncParams(ctx, controller, controller[funcName]));}catch(err){console.error(err);next(); } }private buildFuncParams(ctx: any, controller: any, func: Function) { //把參數(shù)具體的值收集起來let paramsInfo = controller[Router][func.name].params;let params = [];if(paramsInfo){for(let i = 0; i < paramsInfo.length; i++) {if(paramsInfo[i]){params.push(paramsInfo[i].type(this.getParam(ctx, paramsInfo[i].paramType, paramsInfo[i].name)));} else {params.push(ctx);}}}return params; }private getParam(ctx: any, paramType: ParamType, name: string){ // 從ctx里把需要的參數(shù)拿出來switch(paramType){case ParamType.Query:return ctx.query[name];case ParamType.Path:return ctx.params[name];case ParamType.Body:return ctx.request.body;default:console.error('does not support this param type');} }

    這樣就完成了簡單版的類似WebApi的路由,源碼在https://github.com/brookshi/webapi-router,歡迎大家Fork/Star,謝謝。

    轉(zhuǎn)載于:https://www.cnblogs.com/brookshi/p/6446120.html

    總結(jié)

    以上是生活随笔為你收集整理的【开源】NodeJS仿WebApi路由的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。