"Open source" Nodejs imitation Webapi routing

Source: Internet
Author: User
Tags export class

WEBAPI or ASP. NET MVC knows that Microsoft's routing is very good, convenient and flexible. Although the individual seems to be too flexible, the different development within the team is easy to use different routing methods and seems a bit confusing. But this is not the point, I do the node project when I feel that the use of the use(...) specified routing path is very annoying, so Typescript write this based on Koa Koa-router the routing plug-in, you can easily implement some similar webapi-like routing capabilities.

The goal is the same as Webapi:

    1. The controller added will automatically join the route.
    2. Routes can also be specified manually via path ().
    3. You can define HTTP method, such as or, and GET POST so on.
    4. The parameters of the API can specify query param, path param, and body in the URL.

The package has been uploaded to NPM, npm install Webapi-router installation, you can first look at the effect:

The first step is to set a fixed prefix for the controllers directory and URL.

All controllers are in this directory, which automatically calculates the route based on the physical path. The fixed prefix of the URL is between the host and the route, for example localhost/api/v2/user/name , api/v2 the fixed prefix.

import { WebApiRouter } from ‘webapi-router‘;app.use(new WebApiRouter().router(‘sample/controllers‘, ‘api‘));
The second step is that the controller BaseController inherits from
export class TestController extends BaseController{}
The third step is to add a decorator to the controller's method.
@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‘;}

@POSTparameter is optional, and the physical path of the controller is used as the routing address for Null.

:nameis the variable in the path, for example, it /user/brook :name can be brook used in the parameters of the method. @PathParam

@QueryParamYou can url get ? the parameters in the back.

@BodyParamCan get Post up thebody

Is it a little webapi?

now, let's see how it all came true.

The implementation process is very simple, starting from the above goal, first get the physical path of the controllers, and then get the decoration of the method and its parameters.
The object of the adorner is whether to get it, to Get Post wait, to specify it Path , and finally to assign the data in node request to the parameters of the method.

Core code:

get the physical path
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);        }    });}
turn from physical path to Route
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;}
the implementation of the adorner

Adorners need to be introduced into the reflect-metadata library

First look at the method of the adorner, @GET and the @POST like, the implementation method is to decorate the method with a property Router , Router is a Symbol , to ensure that the only. Then the function of the analysis decoration is stored in this attribute, for example Method , Path etc.

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

There are also parametric decorators, which are used to assign values to parameters request , such as body , and param so on.

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

This data is stored on the router property of the object, which can be used when building the route later.

The bindings are routed to Koa-router the top

The above is routed from the physical path, but the parameter path in the decoration is preferred, so first look at the properties of the prototype in existence there is Router no Path , some words use this as a route, there is no Path physical route.

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

Assign a value to the method parameter in the controller and bind the route toKoaRouter

Private Setrouterforfunction (method:string, Controller:any, funcname:string, routerpath:string) {This.koaRouter[met Hod] (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> {//this is The line Controller's API method is the try {ctx.body = await controller[funcname] (... this.buildfuncparams (CTX, Controller, CONTR    Oller[funcname]));        } catch (Err) {console.error (err);     Next (); }}private buildfuncparams (Ctx:any, Controller:any, func:function) {//to collect the parameter specific values let Paramsinfo = Controller[router    ][func.name].params;    let params = []; if (Paramsinfo) {for (Let i = 0; i < paramsinfo.length; i++) {if (Paramsinfo[i]) {PA            Rams.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) {//Take the required parameters out of CTX switch (paramtype) {case Paramt Ype.        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 '); }}

This completes the simple version of similar WEBAPI routing, source code in Https://github.com/brookshi/webapi-router, welcome everyone fork/star, thank you.

"Open source" Nodejs imitation Webapi routing

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.