2012-06-06 17 views
14

Sto facendo un progetto in Node.js utilizzando express. Ecco la mia struttura di directory:Caricamento dinamico dei moduli Node.js in base al percorso

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

La directory lib/api/ ha un certo numero di file JS relativi alle API. Quello che devo fare è creare una sorta di sistema di aggancio, che ogni volta che una delle funzioni API viene richiesta dal server HTTP express, fa qualunque azione sia specificata nel gestore API corrispondente. Probabilmente è confusionario, ma spero che tu ne abbia l'idea.

  1. Larry invia la richiesta tramite POST per ottenere i dettagli dell'utente.
  2. Il server cerca in lib/api per trovare la funzione associata a tale richiesta.
  3. Il server esegue l'operazione e invia i dati a Larry.

Speriamo che tu possa darmi una mano. Pensavo che si potesse fare usando i prototipi, non è sicuro.

Grazie!

+0

Non sono sicuro di aver capito. Devi semplicemente 'var m = require ('./ lib/api/user_getDetails.js')' e usa quel modulo nella tua risposta. Mi sto perdendo qualcosa? – freakish

+0

Lo voglio caricare dinamicamente. Cioè, se aggiungo una nuova funzione API, non devo richiederlo manualmente. Ma non sono sicuro di come farlo. –

+0

Quindi, vuoi un autoloader? –

risposta

23

Se sapete dove i vostri script sono, per esempio, si dispone di una directory iniziale, ad esempio DIR, allora si può lavorare con fs, ad esempio:

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 

Ora gli script devono seguire la seguente struttura (a causa della riga require(path)(module_holder)), ad esempio:

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

ed ora, durante la manipolazione di una richiesta, dovete fare:

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

Questo dovrebbe caricare tutti i moduli per module_holder variabile. Non l'ho provato, ma dovrebbe funzionare (tranne per la gestione degli errori !!!). Potresti voler modificare questa funzione (ad esempio, rendere module_holder un albero, non un dizionario di un livello) ma penso che capirai l'idea.

Questa funzione dovrebbe caricarsi una volta per avvio del server (se è necessario attivarlo più spesso, probabilmente si tratta di script dinamici sul lato server e questa è un'idea baaaaaad, imho). L'unica cosa di cui hai bisogno ora è esportare l'oggetto module_holder in modo che ogni gestore di visualizzazione possa usarlo.

+1

Se si sta chiamando la funzione una sola volta all'avvio, non c'è motivo di utilizzare le versioni asincrone; usa solo 'fs.lstatSync' e' fs.readdirSync'. Questo ti impedisce anche di ingoiare errori poiché verranno lanciate eccezioni, invece degli errori passati ai callback e quindi ignorati. – Domenic

+0

@Domenico Vero. In qualche modo mi sono abituato alla programmazione asincrona e non penso più in modo sincrono, hehe. :) A proposito, hai rimosso il mio blocco 'try {} catch {}'. In effetti, non è necessario qui, poiché lo script continuerà a funzionare anche se il modulo genera un'eccezione. Ma questo non è più vero per le versioni sincrone! – freakish

+0

Ottima risposta, ma sono ancora confuso su come rendere il server utilizza i moduli ... Una soluzione ideale sarebbe in qualche modo utilizzare un sistema basato su prototipi che posso estendere (se questo ha senso). Qualsiasi carico di script dovrebbe avere qualcosa come 'Hook.add (" user_getDetails "); Hook.user_getDetails.action = function() {console.log ("metodo user_getDetails richiamato!")}; ' Qualcosa del genere, anche se non ho idea di come funzioni: 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');  
    } 
}; 

dinamizzare questo codice un po 'si possono fare cose magiche: D

+1

Penso che dovrebbe essere se (typeof (controller [metodo]) == 'function') – zephyr

+0

hai ragione @zephyr – ZiTAL

1

Ecco un esempio di un servizio Web API REST che carica dinamicamente il file del gestore js in base alla URL inviato al server :

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

gestori nel mio caso sono in una sottocartella/TrainerCentral, quindi la mappatura funziona così:

localhost: 8080/TrainerCentral/Ricetta verrà convertito il file js/TrainerCentral /Recipe.js localhost: 8080/TrainerCentral/Workout verrà convertito /TrainerCentral/Workout.js di file js

ecco un gestore esempio in grado di gestire ciascuna delle azioni HTTP principali 4 per il recupero, l'inserimento, l'aggiornamento, e cancellazione dei dati.

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

avviare il server dalla riga di comando con "index.js nodo"

Buon divertimento!

Problemi correlati