Javascript 基于路由动态加载 Node.js 模块

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10914751/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 03:33:13  来源:igfitidea点击:

Loading Node.js modules dynamically based on route

javascriptapinode.jsprototypev8

提问by freakish

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. Server looks in lib/apito find the function associated with that request.
  3. Server carrys out action and sends back data to Larry.
  1. Larry 通过 POST 发送请求以获取用户详细信息。
  2. 服务器查找lib/api与该请求关联的函数。
  3. 服务器执行操作并将数据发送回 Larry。

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

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

Thanks!

谢谢!

回答by freakish

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

服务器.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_holdervariable. 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_holdera 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_holderobject so that every view handler can use it.

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

回答by ZiTAL

app.js

应用程序.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

回答by JJ_Coder4Hire

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

服务器.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

路由器.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

索引.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!

玩得开心!