web-dev-qa-db-ja.com

ルートに基づいてNode.jsモジュールを動的にロードする

Expressを使用してNode.jsでプロジェクトを実行しています。ディレクトリ構造は次のとおりです。

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

lib/api/ディレクトリには、APIに関連する多くのJSファイルがあります。私がする必要があるのは、一種のフックシステムを作成することです。API関数の1つが高速HTTPサーバーから要求されるたびに、対応するAPIハンドラーで指定されたアクションを実行します。おそらく混乱するかもしれませんが、うまくいけばアイデアが得られます。

  1. LarryはPOST経由でユーザーの詳細を取得するためにリクエストを送信します。
  2. サーバーはlib/apiを使用して、その要求に関連付けられている機能を見つけます。
  3. サーバーはアクションを実行し、Larryにデータを送り返します。

うまくいけば、私を助けてくれます。プロトタイプを使用してそれができると考えていましたが、確かではありません。

ありがとう!

22
user775171

スクリプトの場所がわかっている場合、つまりDIRなどの初期ディレクトリがある場合は、fsを使用して作業できます。次に例を示します。

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
_

ここで、スクリプトは次の構造に従う必要があります(require(path)(module_holder)行のため):

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

そして今、リクエストを処理するとき、あなたは:

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

これにより、すべてのモジュールが_module_holder_変数にロードされます。私はそれをテストしませんでしたが、動作するはずです(エラー処理を除く!!!)。この関数を変更したい場合があります(たとえば、_module_holder_を1レベルの辞書ではなくツリーにします)が、その考えは理解できると思います。

この関数は、サーバーの起動ごとに1回ロードする必要があります(より頻繁に起動する必要がある場合は、おそらく動的なサーバー側のスクリプトを扱っているので、これは馬鹿げたアイデアです、私見)。今必要なのは、すべてのビューハンドラが使用できるように_module_holder_オブジェクトをエクスポートすることです。

29
freakish

app.js

var c_file = 'html.js';

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

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

html.js

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

このコードを少し動的にすると、魔法のようなことができます:D

4
ZiTAL

以下は、サーバーに送信されたURLに基​​づいてハンドラーjsファイルを動的にロードするREST API Webサービスの例です。

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

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

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

server.start(8080, router.route);

私の場合、ハンドラーはサブフォルダー/ TrainerCentralにあるため、マッピングは次のように機能します。

localhost:8080/TrainerCentral/Recipeはjsファイル/TrainerCentral/Recipe.jsにマップされますlocalhost:8080/TrainerCentral/Workoutはjsファイル/TrainerCentral/Workout.jsにマップされます

以下に、データを取得、挿入、更新、削除するための4つのメインHTTPアクションのそれぞれを処理できるハンドラーの例を示します。

/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");
   }
};

「node index.js」を使用してコマンドラインからサーバーを起動します

楽しんで!

2
JJ_Coder4Hire