2012-06-06 14 views
14

Estoy haciendo un proyecto en Node.js usando express. Aquí está mi estructura de directorios:Cargando módulos Node.js dinámicamente según la ruta

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

El directorio lib/api/ tiene una serie de archivos JS relativos a la API. Lo que tengo que hacer es crear una especie de sistema de enganche, que cada vez que una de las funciones de la API sea solicitada desde el servidor HTTP expreso, haga cualquier acción que se especifique en el controlador API correspondiente. Es probable que sea confuso, pero espero que entiendas la idea.

  1. Larry envía la solicitud a través de POST para obtener los detalles del usuario.
  2. El servidor se ve en lib/api para encontrar la función asociada con esa solicitud.
  3. El servidor lleva a cabo una acción y devuelve datos a Larry.

Espero que me puedan ayudar. Estaba pensando que podría hacerse usando prototipos, aunque no estoy seguro.

Gracias!

+0

No estoy seguro de entender. Simplemente haga 'var m = require ('./ lib/api/user_getDetails.js')' y use ese módulo en su respuesta. ¿Me estoy perdiendo de algo? – freakish

+0

Quiero que cargue dinámicamente. Es decir, si agrego una nueva función de API, no tengo que solicitarla manualmente. Pero no estoy seguro de cómo lograr esto. –

+0

Entonces, ¿quieres un autocargador? –

Respuesta

23

Si sabe dónde están sus guiones son, es decir, que tiene un directorio inicial, por ejemplo DIR, a continuación, se puede trabajar con fs, por ejemplo:

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 

Ahora sus scripts deben seguir la siguiente estructura (debido a la línea require(path)(module_holder)), por ejemplo:

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

y ahora, al manejar una solicitud, trata de:

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

Esto debe cargar todos los módulos de module_holder variable. No lo probé, pero debería funcionar (excepto por el manejo de errores !!!). Es posible que desee alterar esta función (por ejemplo, haga de module_holder un árbol, no un diccionario de un nivel), pero creo que captará la idea.

Esta función debe cargarse una vez por cada inicio del servidor (si necesita dispararla más a menudo, entonces probablemente se trate de secuencias de comandos dinámicas del lado del servidor y esta es una idea increíble, imho). Lo único que necesita ahora es exportar el objeto module_holder para que cada controlador de vista pueda usarlo.

+1

Si está llamando a la función una sola vez en el inicio, no hay razón para usar las versiones asíncronas; solo use 'fs.lstatSync' y' fs.readdirSync'. Eso también evita que cometa errores ya que se lanzarán excepciones, en lugar de errores pasados ​​a devoluciones de llamadas y luego ignorados. – Domenic

+0

@Domenic True. De alguna manera me acostumbré a la programación asincrónica y ya no pienso sincrónicamente, jeje. :) Por cierto, eliminaste mi bloque 'try {} catch {}'. De hecho, no es necesario aquí, ya que la secuencia de comandos continuará funcionando incluso si el módulo arroja una excepción. ¡Pero esto ya no es así para las versiones sincrónicas! – freakish

+0

Excelente respuesta, pero todavía estoy confundido sobre cómo hacer que el servidor use los módulos ... Una solución ideal sería usar de alguna manera un sistema basado en prototipos que pueda extender (si eso tiene sentido). Cualquier carga de script debería tener algo como 'Hook.add (" user_getDetails "); Hook.user_getDetails.action = function() {console.log ("método user_getDetails invocado!")}; ' Algo así, aunque no tengo idea de cómo funcionaría esto: 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');  
    } 
}; 

Dinamizar el código un poco se pueden hacer cosas mágicas: D

+1

Creo que debería ser si (typeof (controller [method]) == 'function') – zephyr

+0

tienes razón @zephyr – ZiTAL

1

Aquí está un ejemplo de un servicio web REST API que carga dinámicamente archivo del controlador de js en base a la URL enviada al servidor :

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

manipuladores en mi caso están en una subcarpeta/TrainerCentral, por lo que la asignación funciona así:

localhost: 8080/TrainerCentral/Receta asignará a js/TrainerCentral /Recipe.js localhost: 8080/TrainerCentral/entrenamiento asignará a /TrainerCentral/Workout.js archivo JS

aquí es un controlador de ejemplo que puede manejar cada uno de los 4 principales acciones HTTP para recuperar, insertar, actualizar y eliminando datos.

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

iniciar el servidor desde la línea de comandos con "nodo" index.js

Diviértete!

Cuestiones relacionadas