Express: Wie übergebe ich eine App-Instanz an Routen aus einer anderen Datei?


103

Ich möchte meine Routen in verschiedene Dateien aufteilen, wobei eine Datei alle Routen und die andere die entsprechenden Aktionen enthält. Ich habe derzeit eine Lösung, um dies zu erreichen. Ich muss jedoch die App-Instanz global machen, um in den Aktionen darauf zugreifen zu können. Mein aktuelles Setup sieht folgendermaßen aus:

app.js:

var express   = require('express');
var app       = express.createServer();
var routes    = require('./routes');

var controllers = require('./controllers');
routes.setup(app, controllers);

app.listen(3000, function() {
  console.log('Application is listening on port 3000');
});

route.js:

exports.setup = function(app, controllers) {

  app.get('/', controllers.index);
  app.get('/posts', controllers.posts.index);
  app.get('/posts/:post', controllers.posts.show);
  // etc.

};

controller / index.js:

exports.posts = require('./posts');

exports.index = function(req, res) {
  // code
};

controller / posts.js:

exports.index = function(req, res) {
  // code
};

exports.show = function(req, res) {
  // code
};

Dieses Setup hat jedoch ein großes Problem: Ich habe eine Datenbank- und eine App-Instanz, die ich an die Aktionen übergeben muss (Controller / *. Js). Die einzige Option, die ich mir vorstellen kann, besteht darin, beide Variablen global zu machen, was eigentlich keine Lösung ist. Ich möchte Routen von den Aktionen trennen, weil ich viele Routen habe und sie an einem zentralen Ort haben möchte.

Was ist der beste Weg, um Variablen an die Aktionen zu übergeben, aber die Aktionen von den Routen zu trennen?


Wie sieht Ihre controller.js aus? Vielleicht können Sie daraus eine Funktion (anstelle eines Objekts) machen, die Parameter empfangen kann.
Mihai

require ('controller') erfordert controller / index.js. Eine Funktion funktioniert jedoch nicht, da ich das Objekt in den Routen verwende (siehe route.js) und daher keine Argumente an sie übergeben kann, selbst wenn es sich um eine Funktion handelt.
Claudio Albertin

Antworten:


165

Verwenden Sie req.app,req.app.get('somekey')

Die durch Aufrufen erstellte Anwendungsvariable express()wird für die Anforderungs- und Antwortobjekte festgelegt.

Siehe: https://github.com/visionmedia/express/blob/76147c78a15904d4e4e469095a29d1bec9775ab6/lib/express.js#L34-L35


Vielen Dank. Ich denke, dies ist der beste Weg, um auf Variablen zuzugreifen, die mit app.set ('name', val) festgelegt wurden.
Pavel Kostenko

4
Vergessen Sie nicht, app.set('somekey', {})app.js
ankitjaininfo

3
Mein einziger Kritikpunkt an diesem Weg, obwohl ich ihn liebe, ist, dass, wenn Sie versuchen, eine als solche autorisierte app.locals.aut (nicht in main.js) auszuführen, dies app.route('/something').get(app.locals.authorized,function(req,res,next){});nicht möglich ist, da sie außerhalb des erforderlichen Bereichs liegt.
gabeio

Ich verwende unterschiedliche Passstrategien für unterschiedliche Abfrageparameter. Ich versuche also, passport.use ("Strategie-Name") in einer Middleware festzulegen. Auch wenn ich den Pass in dieser Middleware nur mit let passport = req.app speichere, get ('passport'). Es wird für einen anderen Satz von Anforderungen geändert. Wieso ist es so ?
Kartikeya Mishra

Wenn ich dies tue, hat das req-Objekt in meinem Fall zusätzliche Objektinstanzen wie redis und db. Beeinflusst es nicht die Anwendungsleistung? zB: in index.js app.set ('redis', redis_client); in routen / example.js router = require ('express'). Router (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); return res.send ("// done");})
Suz Aann Shrestha 12.

101

Node.js unterstützt zirkuläre Abhängigkeiten.
Durch die Verwendung von zirkulären Abhängigkeiten anstelle von require ('./ route') (App) wird viel Code bereinigt und jedes Modul ist weniger abhängig von seiner Ladedatei:


app.js.

var app = module.exports = express(); //now app.js can be required to bring app into any file

//some app/middleware setup, etc, including 
app.use(app.router);

require('./routes'); //module.exports must be defined before this line


routen / index.js

var app = require('../app');

app.get('/', function(req, res, next) {
  res.render('index');
});

//require in some other route files...each of which requires app independently
require('./user');
require('./blog');


----- 04/2014 Update -----
Express 4.0 hat den Anwendungsfall für die Definition von Routen durch Hinzufügen einer express.router () -Methode behoben!
Dokumentation - http://expressjs.com/4x/api.html#router

Beispiel aus ihrem neuen Generator:
Schreiben der Route:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Hinzufügen / Namespace der App: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Es gibt immer noch Anwendungsfälle für den Zugriff auf Apps von anderen Ressourcen aus, sodass zirkuläre Abhängigkeiten immer noch eine gültige Lösung sind.


1
"weniger voneinander abhängig von der Ladedatei " - dies hängt vom spezifischen Dateipfad der Ladedatei ab . Das ist eine sehr enge Kopplung, also tun wir nicht so, als wäre es nicht so.
Camilo Martin

2
Seien Sie nur sehr vorsichtig (lesen Sie: Tun Sie nicht das, womit ich in der letzten Stunde zu kämpfen hatte +), dass app.jsSie nach dem Exportieren der App die Routing-Datei benötigen . Zirkuläre require()Anrufe können zu einem echten Durcheinander führen. Stellen Sie also sicher, dass Sie wissen, wie sie funktionieren !
Nateowami

Ich denke ehrlich, dass die Antwort von @Feng über die Verwendung von req.app.get ('somekey') in der Tat eine viel bessere und sauberere Lösung ist als die Verwendung von Circulr-Abhängigkeiten.
Claudio Mezzasalma

@Green Wenn die App leer ist, benötigen Sie eine Datei, die appVOR der module.exportsDefinition der App erforderlich ist . Sie müssen Dateien instanziieren app, festlegen module.exportsund dann anfordern, die möglicherweise erforderlich sind. app In beiden Fällen ist das Ausführen der zirkulären Abhängigkeiten jedoch ein Anti-Pattern, das Express gelöst hat - das sollten Sie nicht mehr tun müssen.
Will Stern

26

Wie ich in den Kommentaren sagte, können Sie eine Funktion als module.exports verwenden. Eine Funktion ist auch ein Objekt, sodass Sie Ihre Syntax nicht ändern müssen.

app.js.

var controllers = require('./controllers')({app: app});

controller.js

module.exports = function(params)
{
    return require('controllers/index')(params);
}

controller / index.js

function controllers(params)
{
  var app = params.app;

  controllers.posts = require('./posts');

  controllers.index = function(req, res) {
    // code
  };
}

module.exports = controllers;

Ist es in Ordnung, ein Objekt innerhalb der Funktion zurückzugeben, oder ist es besser, die Methoden so festzulegen, wie Sie es in Ihrem Beispiel tun?
Claudio Albertin

Ich denke, jeder Ansatz ist in Ordnung.
Mihai

Da ich viele Methoden habe, würde ich es vorziehen, sie als Objekt anstatt manuell festzulegen. Dies würde funktionieren, wenn ich das Objekt nur zurückgebe, aber gibt es keine Lösung, die etwas flacher ist? Meine tatsächlichen Methoden würden zweimal eingerückt ...
Claudio Albertin

Ich bin mir nicht sicher, ob ich Sie verstanden habe, aber ich denke, Sie könnten die Implementierung außerhalb dieser controllersFunktion verschieben, etwa: jsfiddle.net/mihaifm/yV79K
mihai

müssen controller / index.js nicht controller var zurückgeben?
Yalamber

5

Oder mach das einfach:

var app = req.app

in der Middleware, die Sie für diese Routen verwenden. So wie das:

router.use( (req,res,next) => {
    app = req.app;
    next();
});

Jemand sagt mir, warum dies nicht die akzeptierte Antwort ist? Für Abhängigkeiten verwenden Sie app.use('my-service', serviceInstance)im Hauptrouter und req.app.get('my-service')im Controller, wie von @Feng
Felipe

0

Angenommen, Sie haben einen Ordner mit dem Namen "contollers".

In Ihre app.js können Sie diesen Code einfügen:

console.log("Loading controllers....");
var controllers = {};

var controllers_path = process.cwd() + '/controllers'

fs.readdirSync(controllers_path).forEach(function (file) {
    if (file.indexOf('.js') != -1) {
        controllers[file.split('.')[0]] = require(controllers_path + '/' + file)
    }
});

console.log("Controllers loaded..............[ok]");

... und ...

router.get('/ping', controllers.ping.pinging);

In Ihrem Controller-Forlder befindet sich die Datei "ping.js" mit folgendem Code:

exports.pinging = function(req, res, next){
    console.log("ping ...");
}

Und das ist es....


0
  1. So machen Sie Ihr DB-Objekt für alle Controller zugänglich, ohne es überall zu übergeben: Erstellen Sie eine Middleware auf Anwendungsebene, die das DB-Objekt an jedes erforderliche Objekt anfügt, und greifen Sie dann in jedem Controller darauf zu.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. Um zu vermeiden, dass die App-Instanz überall übergeben wird, müssen Routen an den Ort übergeben werden, an dem sich die App befindet
// routes.js  It's just a mapping.
exports.routes = [
  ['/', controllers.index],
  ['/posts', controllers.posts.index],
  ['/posts/:post', controllers.posts.show]
];

// app.js
var { routes }    = require('./routes');
routes.forEach(route => app.get(...route));
// You can customize this according to your own needs, like adding post request

Die endgültige app.js:

// app.js
var express   = require('express');
var app       = express.createServer();

let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);

var { routes }    = require('./routes');
routes.forEach(route => app.get(...route));

app.listen(3000, function() {
  console.log('Application is listening on port 3000');
});

Eine andere Version: Sie können dies an Ihre eigenen Bedürfnisse anpassen, z. B. das Hinzufügen einer Post-Anfrage

// routes.js  It's just a mapping.
let get = ({path, callback}) => ({app})=>{
  app.get(path, callback);
}
let post = ({path, callback}) => ({app})=>{
  app.post(path, callback);
}
let someFn = ({path, callback}) => ({app})=>{
  // ...custom logic
  app.get(path, callback);
}
exports.routes = [
  get({path: '/', callback: controllers.index}),
  post({path: '/posts', callback: controllers.posts.index}),
  someFn({path: '/posts/:post', callback: controllers.posts.show}),
];

// app.js
var { routes }    = require('./routes');
routes.forEach(route => route({app}));

-1

Für die Datenbank trennen Sie den Datenzugriffsdienst, der die gesamte Datenbank mit einer einfachen API ausführt und den gemeinsamen Status vermeidet.

Das Trennen von route.setup sieht nach Overhead aus. Ich würde stattdessen lieber ein konfigurationsbasiertes Routing platzieren. Und konfigurieren Sie Routen in .json oder mit Anmerkungen.


Was meinen Sie mit einem Datenzugriffsdienst? Wie würde es aussehen?
Claudio Albertin

Meine echte Datei route.js ist viel größer und verwendet das Express-Namespaces-Modul. Wie würden Sie die Routen von den Aktionen trennen?
Claudio Albertin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.