2012-06-06 11 views
14

Robię projekt w Node.js za pomocą ekspresowego. Oto moja struktura katalogów:Ładowanie modułów Node.js dynamicznie w oparciu o trasę

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

Katalog lib/api/ ma liczbę plików JS dotyczących API. To, co muszę zrobić, to stworzyć system zahaczania, że ​​gdy tylko jedna z funkcji API zostanie zażądana z serwera ekspresowego HTTP, robi to, co zostało określone w odpowiedniej procedurze API. Prawdopodobnie jest to mylące, ale mam nadzieję, że wpadniesz na ten pomysł.

  1. Larry wysyła żądanie przez POST, aby uzyskać dane użytkownika.
  2. Serwer wyszukuje w lib/api funkcję związaną z tym żądaniem.
  3. Serwer wykonuje akcję i odsyła dane do Larry'ego.

Mam nadzieję, że możesz mi pomóc. Myślałem, że można to zrobić za pomocą prototypów, nie jestem jednak pewien.

Dzięki!

+0

Nie jestem pewien, czy rozumiem. Po prostu robisz 'var m = require ('./ lib/api/user_getDetails.js') i używasz tego modułu w swojej odpowiedzi. Czy czegoś brakuje? – freakish

+0

Chcę, aby ładował się dynamicznie. Oznacza to, że jeśli dodaję nową funkcję API, nie muszę jej ręcznie wymagać. Ale nie jestem pewien, jak to osiągnąć. –

+0

A więc chcesz autoloadera? –

Odpowiedz

23

Jeśli wiesz, gdzie są skrypty, czyli masz początkowy katalog, na przykład DIR, to może pracować z fs, na przykład:

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 

teraz Twoje skrypty muszą mieć następującą strukturę (ze względu na linię require(path)(module_holder)), na przykład:

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

i teraz, podczas przetwarzania żądania, robisz:

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

ten powinien załadować wszystkie moduły module_holder zmiennej. Nie testowałem tego, ale powinno działać (, z wyjątkiem obsługi błędów !!!). Możesz zmienić tę funkcję (na przykład uczynić module_holder drzewo, a nie słownik jednego poziomu), ale myślę, że zrozumiesz ten pomysł.

Ta funkcja powinna być ładowana raz na start serwera (jeśli chcesz uruchamiać go częściej, prawdopodobnie masz do czynienia z dynamicznymi skryptami po stronie serwera, a to jest pomysł baaaaad, imho). Jedyne, czego potrzebujesz teraz, to wyeksportować obiekt module_holder, aby mógł go użyć każdy moduł obsługi widoku.

+1

Jeśli wywołasz funkcję po uruchomieniu jednorazowo, nie ma powodu, aby używać wersji asynchronicznych; po prostu użyj 'fs.lstatSync' i' fs.readdirSync'. To również powstrzymuje cię od połknięcia błędów, ponieważ będą odrzucane wyjątki zamiast błędów przekazywanych do wywołań zwrotnych, a następnie ignorowane. – Domenic

+0

@ Dentalic True. Jakoś przyzwyczaiłem się do asynchronicznego programowania i już nie myślę synchronicznie, hehe. :) Przy okazji, usunąłeś mój blok 'try {} catch {}'. W rzeczywistości nie jest to konieczne, ponieważ skrypt będzie działał, nawet jeśli moduł zgłasza wyjątek. Ale nie jest to już prawdą w przypadku wersji synchronicznych! – freakish

+0

Świetna odpowiedź, ale nadal jestem zdezorientowany, jak sprawić, by serwer korzystał z modułów ... Idealnym rozwiązaniem byłoby wykorzystanie systemu opartego na prototypach, który mogę rozszerzyć (jeśli to ma sens). Niezależnie od tego, który skrypt ładuje, powinien mieć coś w rodzaju 'Hook.add (" user_getDetails "); Hook.user_getDetails.action = function() {console.log ("metoda user_getDetails invaged!")}; ' Coś takiego, chociaż nie mam pojęcia, jak to zadziała: P –

3

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

zdynamizowania ten kod trochę można robić magiczne rzeczy: D

+1

Myślę, że powinno być, jeśli (typeof (controller [method]) == "function") – zephyr

+0

masz rację @ zefyr – ZiTAL

1

Oto przykład z serwisu WWW REST API, które dynamicznie ładuje plik handler'a js na podstawie adresu URL przesyłane do serwera :

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

koparki w moim przypadku są w podkatalogu/TrainerCentral, więc odwzorowanie działa tak:

localhost: 8080/TrainerCentral/Przepis będzie mapować do pliku js/TrainerCentral /Recipe.js localhost: 8080/TrainerCentral/Workout będzie mapować do plików js /TrainerCentral/Workout.js

tutaj jest przykład, który może obsługiwać obsługi każdego z 4 głównych działań HTTP do pobierania, wstawiania, aktualizacji i usuwanie danych.

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

uruchomić serwer z wiersza poleceń z "index.js Node"

Baw się!

Powiązane problemy