根据路由动态加载Node.js模块

时间:2022-10-22 16:58:07

I'm doing a project in Node.js using express. Here's my directory structure:

我正在使用express在Node.js中做一个项目。这是我的目录结构:

root
|-start.js
|-server.js
|-lib/
|    api/
|        user_getDetails.js
|        user_register.js

The lib/api/ directory has a number of JS files relating to the API. What I need to do is make a sort of hooking system, that whenever one of the API functions gets requested from the express HTTP server, it does whatever action is specified in the according API handler. It's probably confusing, but hopefully you get the idea.

lib / api /目录有许多与API相关的JS文件。我需要做的是制作一种挂钩系统,无论何时从快速HTTP服务器请求其中一个API函数,它都会执行相应API处理程序中指定的任何操作。这可能令人困惑,但希望你能得到这个想法。

  1. Larry sends request via POST to get user details.
  2. Larry通过POST发送请求以获取用户详细信息。
  3. Server looks in lib/api to find the function associated with that request.
  4. 服务器在lib / api中查找与该请求关联的功能。
  5. Server carrys out action and sends back data to Larry.
  6. 服务器执行操作并将数据发送回Larry。

Hopefully you can help me out. I was thinking it could be done using prototypes, not sure though.

希望你能帮助我。我当时认为可以使用原型来完成,但不确定。

Thanks!

谢谢!

3 个解决方案

#1


25  

If you know where your scripts are, i.e. you have an initial directory, for example DIR, then you can work with fs, for example:

如果您知道脚本的位置,即您有一个初始目录,例如DIR,那么您可以使用fs,例如:

server.js

server.js

var fs = require('fs');
var path_module = require('path');
var module_holder = {};

function LoadModules(path) {
    fs.lstat(path, function(err, stat) {
        if (stat.isDirectory()) {
            // we have a directory: do a tree walk
            fs.readdir(path, function(err, files) {
                var f, l = files.length;
                for (var i = 0; i < l; i++) {
                    f = path_module.join(path, files[i]);
                    LoadModules(f);
                }
            });
        } else {
            // we have a file: load it
            require(path)(module_holder);
        }
    });
}
var DIR = path_module.join(__dirname, 'lib', 'api');
LoadModules(DIR);

exports.module_holder = module_holder;
// the usual server stuff goes here

Now your scripts need to follow the following structure (because of the require(path)(module_holder) line), for example:

现在你的脚本需要遵循以下结构(因为require(path)(module_holder)行),例如:

user_getDetails.js

user_getDetails.js

function handler(req, res) {
    console.log('Entered my cool script!');
}

module.exports = function(module_holder) {
    // the key in this dictionary can be whatever you want
    // just make sure it won't override other modules
    module_holder['user_getDetails'] = handler;
};

and now, when handling a request, you do:

现在,在处理请求时,您执行以下操作:

// request is supposed to fire user_getDetails script
module_holder['user_getDetails'](req, res);

This should load all your modules to module_holder variable. I didn't test it, but it should work (except for the error handling!!!). You may want to alter this function (for example make module_holder a tree, not a one level dictionary) but I think you'll grasp the idea.

这应该将所有模块加载到module_holder变量。我没有测试它,但它应该工作(除了错误处理!!!)。您可能想要更改此函数(例如,将module_holder设为树,而不是单级字典),但我认为您将掌握这个想法。

This function should load once per server start (if you need to fire it more often, then you are probably dealing with dynamic server-side scripting and this is a baaaaaad idea, imho). The only thing you need now is to export module_holder object so that every view handler can use it.

这个函数应该在每个服务器启动时加载一次(如果你需要更频繁地启动它,那么你可能正在处理动态服务器端脚本,这是一个baaaaaad想法,imho)。您现在唯一需要的是导出module_holder对象,以便每个视图处理程序都可以使用它。

#2


3  

app.js

app.js

var c_file = 'html.js';

var controller = require(c_file);
var method = 'index';

if(typeof(controller[method])==='function')
    controller[method]();

html.js

html.js

module.exports =
{
    index: function()
    {
        console.log('index method');
    },
    close: function()
    {
        console.log('close method');    
    }
};

dynamizing this code a little bit you can do magic things :D

动态化这段代码你可以做一些神奇的事情:D

#3


1  

Here is an example of a REST API web service that dynamically loads the handler js file based on the url sent to the server:

以下是REST API Web服务的示例,该服务根据发送到服务器的url动态加载处理程序js文件:

server.js

server.js

var http = require("http");
var url = require("url");

function start(port, route) {
   function onRequest(request, response) {
       var pathname = url.parse(request.url).pathname;
       console.log("Server:OnRequest() Request for " + pathname + " received.");
       route(pathname, request, response);
   }

   http.createServer(onRequest).listen(port);
   console.log("Server:Start() Server has started.");
}

exports.start = start;

router.js

router.js

function route(pathname, req, res) {
    console.log("router:route() About to route a request for " + pathname);

    try {
        //dynamically load the js file base on the url path
        var handler = require("." + pathname);

        console.log("router:route() selected handler: " + handler);

        //make sure we got a correct instantiation of the module
        if (typeof handler["post"] === 'function') {
            //route to the right method in the module based on the HTTP action
            if(req.method.toLowerCase() == 'get') {
                handler["get"](req, res);
            } else if (req.method.toLowerCase() == 'post') {
                handler["post"](req, res);
            } else if (req.method.toLowerCase() == 'put') {
                handler["put"](req, res);
            } else if (req.method.toLowerCase() == 'delete') {
                handler["delete"](req, res);
            }

            console.log("router:route() routed successfully");
            return;
        } 
    } catch(err) {
        console.log("router:route() exception instantiating handler: " + err);
    }

    console.log("router:route() No request handler found for " + pathname);
    res.writeHead(404, {"Content-Type": "text/plain"});
    res.write("404 Not found");
    res.end();

}

exports.route = route;

index.js

index.js

var server = require("./server");
var router = require("./router");

server.start(8080, router.route);

handlers in my case are in a subfolder /TrainerCentral, so the mapping works like this:

在我的情况下,处理程序位于子文件夹/ TrainerCentral中,因此映射的工作方式如下:

localhost:8080/TrainerCentral/Recipe will map to js file /TrainerCentral/Recipe.js localhost:8080/TrainerCentral/Workout will map to js file /TrainerCentral/Workout.js

localhost:8080 / TrainerCentral / Recipe将映射到js文件/TrainerCentral/Recipe.js localhost:8080 / TrainerCentral / Workout将映射到js文件/TrainerCentral/Workout.js

here is a example handler that can handle each of the 4 main HTTP actions for retrieving, inserting, updating, and deleting data.

这是一个示例处理程序,可以处理4个主要的HTTP操作,用于检索,插入,更新和删除数据。

/TrainerCentral/Workout.js

/TrainerCentral/Workout.js

function respond(res, code, text) {
    res.writeHead(code, { "Content-Type": "text/plain" });
    res.write(text);
    res.end();
}

module.exports = {
   get: function(req, res) {
       console.log("Workout:get() starting");

       respond(res, 200, "{ 'id': '123945', 'name': 'Upright Rows', 'weight':'125lbs' }");
   },
   post: function(request, res) {
       console.log("Workout:post() starting");

       respond(res, 200, "inserted ok");
   },
   put: function(request, res) {
       console.log("Workout:put() starting");

       respond(res, 200, "updated ok");
   },
   delete: function(request, res) {
       console.log("Workout:delete() starting");

       respond(res, 200, "deleted ok");
   }
};

start the server from command line with "node index.js"

使用“node index.js”从命令行启动服务器

Have fun!

玩的开心!

#1


25  

If you know where your scripts are, i.e. you have an initial directory, for example DIR, then you can work with fs, for example:

如果您知道脚本的位置,即您有一个初始目录,例如DIR,那么您可以使用fs,例如:

server.js

server.js

var fs = require('fs');
var path_module = require('path');
var module_holder = {};

function LoadModules(path) {
    fs.lstat(path, function(err, stat) {
        if (stat.isDirectory()) {
            // we have a directory: do a tree walk
            fs.readdir(path, function(err, files) {
                var f, l = files.length;
                for (var i = 0; i < l; i++) {
                    f = path_module.join(path, files[i]);
                    LoadModules(f);
                }
            });
        } else {
            // we have a file: load it
            require(path)(module_holder);
        }
    });
}
var DIR = path_module.join(__dirname, 'lib', 'api');
LoadModules(DIR);

exports.module_holder = module_holder;
// the usual server stuff goes here

Now your scripts need to follow the following structure (because of the require(path)(module_holder) line), for example:

现在你的脚本需要遵循以下结构(因为require(path)(module_holder)行),例如:

user_getDetails.js

user_getDetails.js

function handler(req, res) {
    console.log('Entered my cool script!');
}

module.exports = function(module_holder) {
    // the key in this dictionary can be whatever you want
    // just make sure it won't override other modules
    module_holder['user_getDetails'] = handler;
};

and now, when handling a request, you do:

现在,在处理请求时,您执行以下操作:

// request is supposed to fire user_getDetails script
module_holder['user_getDetails'](req, res);

This should load all your modules to module_holder variable. I didn't test it, but it should work (except for the error handling!!!). You may want to alter this function (for example make module_holder a tree, not a one level dictionary) but I think you'll grasp the idea.

这应该将所有模块加载到module_holder变量。我没有测试它,但它应该工作(除了错误处理!!!)。您可能想要更改此函数(例如,将module_holder设为树,而不是单级字典),但我认为您将掌握这个想法。

This function should load once per server start (if you need to fire it more often, then you are probably dealing with dynamic server-side scripting and this is a baaaaaad idea, imho). The only thing you need now is to export module_holder object so that every view handler can use it.

这个函数应该在每个服务器启动时加载一次(如果你需要更频繁地启动它,那么你可能正在处理动态服务器端脚本,这是一个baaaaaad想法,imho)。您现在唯一需要的是导出module_holder对象,以便每个视图处理程序都可以使用它。

#2


3  

app.js

app.js

var c_file = 'html.js';

var controller = require(c_file);
var method = 'index';

if(typeof(controller[method])==='function')
    controller[method]();

html.js

html.js

module.exports =
{
    index: function()
    {
        console.log('index method');
    },
    close: function()
    {
        console.log('close method');    
    }
};

dynamizing this code a little bit you can do magic things :D

动态化这段代码你可以做一些神奇的事情:D

#3


1  

Here is an example of a REST API web service that dynamically loads the handler js file based on the url sent to the server:

以下是REST API Web服务的示例,该服务根据发送到服务器的url动态加载处理程序js文件:

server.js

server.js

var http = require("http");
var url = require("url");

function start(port, route) {
   function onRequest(request, response) {
       var pathname = url.parse(request.url).pathname;
       console.log("Server:OnRequest() Request for " + pathname + " received.");
       route(pathname, request, response);
   }

   http.createServer(onRequest).listen(port);
   console.log("Server:Start() Server has started.");
}

exports.start = start;

router.js

router.js

function route(pathname, req, res) {
    console.log("router:route() About to route a request for " + pathname);

    try {
        //dynamically load the js file base on the url path
        var handler = require("." + pathname);

        console.log("router:route() selected handler: " + handler);

        //make sure we got a correct instantiation of the module
        if (typeof handler["post"] === 'function') {
            //route to the right method in the module based on the HTTP action
            if(req.method.toLowerCase() == 'get') {
                handler["get"](req, res);
            } else if (req.method.toLowerCase() == 'post') {
                handler["post"](req, res);
            } else if (req.method.toLowerCase() == 'put') {
                handler["put"](req, res);
            } else if (req.method.toLowerCase() == 'delete') {
                handler["delete"](req, res);
            }

            console.log("router:route() routed successfully");
            return;
        } 
    } catch(err) {
        console.log("router:route() exception instantiating handler: " + err);
    }

    console.log("router:route() No request handler found for " + pathname);
    res.writeHead(404, {"Content-Type": "text/plain"});
    res.write("404 Not found");
    res.end();

}

exports.route = route;

index.js

index.js

var server = require("./server");
var router = require("./router");

server.start(8080, router.route);

handlers in my case are in a subfolder /TrainerCentral, so the mapping works like this:

在我的情况下,处理程序位于子文件夹/ TrainerCentral中,因此映射的工作方式如下:

localhost:8080/TrainerCentral/Recipe will map to js file /TrainerCentral/Recipe.js localhost:8080/TrainerCentral/Workout will map to js file /TrainerCentral/Workout.js

localhost:8080 / TrainerCentral / Recipe将映射到js文件/TrainerCentral/Recipe.js localhost:8080 / TrainerCentral / Workout将映射到js文件/TrainerCentral/Workout.js

here is a example handler that can handle each of the 4 main HTTP actions for retrieving, inserting, updating, and deleting data.

这是一个示例处理程序,可以处理4个主要的HTTP操作,用于检索,插入,更新和删除数据。

/TrainerCentral/Workout.js

/TrainerCentral/Workout.js

function respond(res, code, text) {
    res.writeHead(code, { "Content-Type": "text/plain" });
    res.write(text);
    res.end();
}

module.exports = {
   get: function(req, res) {
       console.log("Workout:get() starting");

       respond(res, 200, "{ 'id': '123945', 'name': 'Upright Rows', 'weight':'125lbs' }");
   },
   post: function(request, res) {
       console.log("Workout:post() starting");

       respond(res, 200, "inserted ok");
   },
   put: function(request, res) {
       console.log("Workout:put() starting");

       respond(res, 200, "updated ok");
   },
   delete: function(request, res) {
       console.log("Workout:delete() starting");

       respond(res, 200, "deleted ok");
   }
};

start the server from command line with "node index.js"

使用“node index.js”从命令行启动服务器

Have fun!

玩的开心!