Загрузка динамических модулей Node.js на основе маршрута

Я делаю проект в Node.js с помощью выражения. Здесь моя структура каталогов:

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

В каталоге lib/api/ имеется несколько JS файлов, относящихся к API. То, что мне нужно сделать, это сделать какую-то систему подключений, когда всякий раз, когда одна из функций API запрашивается с помощью экспресс-HTTP-сервера, она выполняет любое действие в соответствующем обработчике API. Это, вероятно, запутанно, но, надеюсь, вы получите эту идею.

  • Ларри отправляет запрос через POST для получения информации о пользователе.
  • Сервер ищет в lib/api, чтобы найти функцию, связанную с этим запросом.
  • Сервер выполняет действие и отправляет данные Ларри.

Надеюсь, вы можете мне помочь. Я думал, что это можно сделать с использованием прототипов, но не уверен.

Спасибо!

Ответы

Ответ 1

Если вы знаете, где ваши скрипты, т.е. у вас есть начальный каталог, например 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)), например:

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

и теперь, когда вы обрабатываете запрос, вы делаете:

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

Это должно загрузить все ваши модули в переменную module_holder. Я не тестировал его, но он должен работать (, за исключением обработки ошибок!!!). Вы можете изменить эту функцию (например, make module_holder дерево, а не одноуровневый словарь), но я думаю, вы поймете эту идею.

Эта функция должна загружаться один раз для запуска сервера (если вам нужно чаще запускать ее, то вы, вероятно, имеете дело с динамическим серверным скриптом, и это идея baaaaaad, imho). Теперь вам нужно экспортировать объект module_holder, чтобы каждый обработчик вида мог его использовать.

Ответ 2

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

Ответ 3

Ниже приведен пример веб-службы REST API, которая динамически загружает файл js-обработчика на основе URL-адреса, отправленного на сервер:

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 "

Удачи!