Express: Bagaimana cara meneruskan app-instance ke rute dari file yang berbeda?

103

Saya ingin membagi rute saya menjadi beberapa file berbeda, di mana satu file berisi semua rute dan file lainnya berisi tindakan yang sesuai. Saat ini saya memiliki solusi untuk mencapai ini, namun saya perlu membuat instance aplikasi global untuk dapat mengaksesnya dalam tindakan. Pengaturan saya saat ini terlihat seperti ini:

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

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

Namun, penyiapan ini memiliki masalah besar: Saya memiliki database- dan instance aplikasi yang perlu saya sampaikan ke tindakan (pengontrol / *. Js). Satu-satunya pilihan yang dapat saya pikirkan, adalah membuat kedua variabel global yang sebenarnya bukan solusi. Saya ingin memisahkan rute dari tindakan karena saya memiliki banyak rute dan ingin rute tersebut berada di pusat.

Apa cara terbaik untuk meneruskan variabel ke tindakan tetapi memisahkan tindakan dari rute?

Claudio Albertin
sumber
Bagaimana tampilan controllers.js Anda? Mungkin Anda bisa menjadikannya sebuah fungsi (bukan sebuah objek) yang bisa menerima parameter.
mihai
memerlukan ('pengontrol') membutuhkan pengontrol / index.js. Namun, sebuah fungsi tidak akan berfungsi karena saya menggunakan objek di routes (lihat routes.js) dan dengan demikian tidak bisa meneruskan argumen ke sana, bahkan jika itu sebuah fungsi.
Claudio Albertin

Jawaban:

165

Gunakan req.app,req.app.get('somekey')

Variabel aplikasi yang dibuat dengan memanggil express()disetel pada objek permintaan dan respons.

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

Feng
sumber
Terima kasih. Saya pikir ini adalah cara terbaik untuk mengakses variabel yang disetel dengan app.set ('name', val);
Pavel Kostenko
4
Jangan lupa untuk menelepon app.set('somekey', {})di app.js
ankitjaininfo
3
Satu-satunya keluhan saya tentang cara ini meskipun saya menyukainya adalah bahwa ketika Anda mencoba menjalankan app.locals.authorized seperti itu (bukan di main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});tidak mungkin karena berada di luar ruang lingkup req.
gabeio
Saya menggunakan strategi paspor yang berbeda untuk parameter kueri yang berbeda. Jadi saya mencoba untuk mengatur passport.use ("strategi-nama") di middleware. Bahkan jika saya menyimpan paspor di middleware itu hanya dengan let passport = req.app, get ('passport'). Itu sedang dimodifikasi untuk set permintaan lain. Kenapa gitu ?
Kartikeya Mishra
Jika saya melakukan ini, maka objek req akan memiliki instance Objek tambahan seperti redis dan db dalam kasus saya. Tidakkah itu akan mempengaruhi kinerja aplikasi? misalnya: di index.js app.set ('redis', redis_client); di routes / 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
101

Node.js mendukung dependensi melingkar.
Memanfaatkan dependensi melingkar alih-alih memerlukan ('./ routes') (app) membersihkan banyak kode dan membuat setiap modul kurang saling bergantung pada file pemuatannya:


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


rute / indeks.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');


----- Pembaruan 04/2014 -----
Express 4.0 memperbaiki kasus penggunaan untuk menentukan rute dengan menambahkan metode express.router ()!
dokumentasi - http://expressjs.com/4x/api.html#router

Contoh dari generator baru mereka:
Menulis rute:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Menambahkan / namespacing ke aplikasi: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Masih ada kasus penggunaan untuk mengakses aplikasi dari sumber daya lain, jadi dependensi melingkar masih merupakan solusi yang valid.

Will Stern
sumber
1
"kurang saling bergantung pada file pemuatannya" - ini bergantung pada jalur file tertentu dari file pemuatannya. Itu kopling yang sangat erat, jadi jangan berpura-pura tidak.
Camilo Martin
2
Berhati-hatilah (baca: jangan lakukan apa yang telah saya perjuangkan selama satu jam terakhir +) bahwa app.jsAnda memerlukan file perutean setelah mengekspor aplikasi. require()Panggilan melingkar dapat membuat kekacauan nyata, jadi pastikan Anda tahu cara kerjanya !
Nateowami
Sejujurnya saya berpikir bahwa jawaban dari @Feng tentang penggunaan req.app.get ('somekey') memang merupakan solusi yang jauh lebih baik dan lebih bersih daripada menggunakan dependensi circulr.
Claudio Mezzasalma
@Green jika aplikasi kosong, Anda memerlukan file yang diperlukan appSEBELUM aplikasi module.exportsdidefinisikan. Anda harus membuat instance app, menyetel module.exports, lalu meminta file yang mungkin memerlukan. app Tapi bagaimanapun juga, melakukan dependensi melingkar adalah anti-pola yang telah dipecahkan oleh express - Anda tidak perlu melakukannya lagi.
Akan Stern
26

Seperti yang saya katakan di komentar, Anda dapat menggunakan fungsi sebagai module.exports. Fungsi juga merupakan objek, jadi Anda tidak perlu mengubah sintaksis Anda.

app.js

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

controllers.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;
mihai
sumber
Apakah boleh mengembalikan objek di dalam fungsi atau lebih baik jadi setel metode seperti yang Anda lakukan dalam contoh?
Claudio Albertin
Saya pikir pendekatan mana pun baik-baik saja.
mihai
Karena saya memiliki banyak metode, saya lebih suka menetapkannya sebagai objek daripada masing-masing secara manual. Ini akan berfungsi ketika saya baru saja mengembalikan objek, tetapi bukankah ada solusi yang sedikit lebih datar? Metode saya yang sebenarnya akan menjorok dua kali ...
Claudio Albertin
Tidak yakin apakah saya mengerti Anda, tapi saya rasa Anda dapat memindahkan implementasi di luar controllersfungsi itu, seperti: jsfiddle.net/mihaifm/yV79K
mihai
tidak controller / index.js perlu mengembalikan controller var?
Yalamber
5

Atau lakukan saja:

var app = req.app

di dalam Middleware yang Anda gunakan untuk rute ini. Seperti itu:

router.use( (req,res,next) => {
    app = req.app;
    next();
});
asanchez
sumber
Seseorang memberi tahu saya mengapa ini bukan jawaban yang diterima? Untuk dependensi yang Anda gunakan app.use('my-service', serviceInstance)di router utama dan req.app.get('my-service')di pengontrol seperti yang disebutkan oleh @Feng
Felipe
0

Misalkan Anda memiliki folder bernama "contollers".

Di app.js Anda, Anda dapat meletakkan kode ini:

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

... dan ...

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

di controller forlder Anda, Anda akan memiliki file "ping.js" dengan kode ini:

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

Dan ini dia ....

Radu Gheorghies
sumber
0
  1. Untuk membuat objek db Anda dapat diakses oleh semua pengontrol tanpa meneruskannya ke mana-mana: buat middleware tingkat aplikasi yang melampirkan objek db ke setiap objek req, lalu Anda dapat mengaksesnya di dalam setiap pengontrol.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. untuk menghindari meneruskan instance aplikasi ke mana-mana, sebaliknya, meneruskan rute ke tempat aplikasi berada
// 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

App.js terakhir:

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

Versi lain: Anda dapat menyesuaikan ini sesuai dengan kebutuhan Anda sendiri, seperti menambahkan permintaan posting

// 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}));
menodai
sumber
-1

Untuk database, pisahkan Layanan Akses Data yang akan melakukan semua pekerjaan DB dengan API sederhana dan menghindari status bersama.

Route.setup yang terpisah terlihat seperti overhead. Saya lebih suka menempatkan perutean berbasis konfigurasi. Dan konfigurasikan rute dalam .json atau dengan anotasi.

Eldar Djafarov
sumber
Apa yang Anda maksud dengan Layanan Akses Data? Akan terlihat seperti apa?
Claudio Albertin
File routes.js saya yang sebenarnya jauh lebih besar dan menggunakan modul express-namespaces. Bagaimana Anda memisahkan rute dari tindakan?
Claudio Albertin