Загрузка динамических модулей 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 "
Удачи!