2012-06-06 11 views
14

Ich mache ein Projekt in Node.js mit Express. Hier ist meine Verzeichnisstruktur:Loading Node.js Module dynamisch basierend auf Route

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

Das lib/api/ Verzeichnis hat eine Reihe von JS-Dateien in das API beziehen. Was ich tun muss, ist eine Art von Hook-System, das immer dann, wenn eine der API-Funktionen vom Express-HTTP-Server angefordert wird, die im entsprechenden API-Handler angegebene Aktion ausführt. Es ist wahrscheinlich verwirrend, aber hoffentlich kommst du auf die Idee.

  1. Larry sendet Anfrage über POST, um Benutzerdetails zu erhalten.
  2. Der Server sucht in lib/api nach der Funktion, die dieser Anforderung zugeordnet ist.
  3. Server führt Aktion aus und sendet Daten an Larry zurück.

Hoffentlich können Sie mir helfen. Ich dachte, es könnte mit Prototypen gemacht werden, aber nicht sicher.

Danke!

+0

Ich bin mir nicht sicher, ob ich das verstehe. Sie müssen einfach 'var m = require ('./ lib/api/user_getDetails.js')' und dieses Modul in Ihrer Antwort verwenden. Fehle ich etwas? – freakish

+0

Ich möchte es dynamisch laden. Das heißt, wenn ich eine neue API-Funktion hinzufüge, muss ich sie nicht manuell anfordern. Aber ich bin mir nicht sicher, wie ich das erreichen soll. –

+0

Sie möchten also einen Autoloader? –

Antwort

23

Wenn Sie wissen, wo Ihre Skripte, dh Sie ein erstes Verzeichnis, zum Beispiel DIR, dann können Sie mit fs arbeiten, zum Beispiel:

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 

Jetzt Ihre Skripts müssen der folgenden Struktur folgen (wegen der Zeile require(path)(module_holder)), zum Beispiel:

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

und jetzt, wenn eine Anfrage Handhabung Sie tun:

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

Dies sollte alle Module module_holder Variable laden. Ich habe es nicht getestet, aber es sollte funktionieren (außer der Fehlerbehandlung !!!). Vielleicht möchten Sie diese Funktion ändern (zum Beispiel make module_holder ein Baum, nicht ein Wörterbuch mit einer Ebene), aber ich denke, Sie werden die Idee begreifen.

Diese Funktion sollte einmal pro Serverstart geladen werden (wenn Sie es häufiger auslösen müssen, dann beschäftigen Sie sich wahrscheinlich mit dynamischem serverseitigen Scripting und dies ist eine baaaaaad Idee, imho). Das einzige, was Sie jetzt brauchen, ist das Objekt module_holder zu exportieren, damit jeder View-Handler es verwenden kann.

+1

Wenn Sie die Funktion beim Start ein einziges Mal aufrufen, gibt es keinen Grund, die Async-Versionen zu verwenden; Verwenden Sie einfach 'fs.lstatSync' und' fs.readdirSync'. Das hindert Sie auch daran, Fehler zu verschlucken, da Ausnahmen ausgelöst werden, anstatt dass Fehler an Rückrufe weitergegeben und dann ignoriert werden. – Domenic

+0

@Domenisch Wahr. Irgendwie habe ich mich an asynchrone Programmierung gewöhnt und denke nicht mehr synchron, hehe. :) Übrigens hast du meinen 'try {} catch {}' Block entfernt. Tatsächlich wird es hier nicht benötigt, da das Skript auch dann noch funktioniert, wenn das Modul eine Ausnahme auslöst. Für synchrone Versionen gilt das nicht mehr! – freakish

+0

Große Antwort, aber ich bin immer noch verwirrt darüber, wie man den Server dazu bringt, die Module zu verwenden ... Eine ideale Lösung wäre, irgendwie ein prototypbasiertes System zu verwenden, das ich erweitern kann (wenn das Sinn macht). Was auch immer Skript lädt sollte etwas wie 'Hook.add (" user_getDetails "); Hook.user_getDetails.action = function() {console.log ("user_getDetails Methode aufgerufen!")}; ' So etwas, obwohl ich keine Ahnung habe, wie das funktionieren würde: 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');  
    } 
}; 

dynamisieren diesen Code ein wenig Sie magische Dinge tun kann: D

+1

Ich denke, es sollte sein, wenn (typeof (controller [methode]) == 'funktion') – zephyr

+0

du bist richtig @zephyr – ZiTAL

1

Hier ist ein Beispiel für einen Web-Service-REST-API, die die Handler js Datei basierend auf der URL lädt dynamisch an den Server gesendet :

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

Handler in meinem Fall sind in einem Unterordner/TrainerCentral, so dass die Zuordnung funktioniert wie folgt:

localhost: 8080/TrainerCentral/Rezept js Datei abbildet/TrainerCentral /Recipe.js localhost: 8080/TrainerCentral/Workout zu js Datei /TrainerCentral/Workout.js

hier abbildet sind ein Beispiel-Handler, der jede der vier Haupt-HTTP-Aktionen für das Abrufen, Einfügen, Aktualisieren verarbeiten kann, und Löschen von Daten

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

den Server von der Kommandozeile mit "Knoten index.js" start

Viel Spaß!

Verwandte Themen