Express: Làm thế nào để chuyển phiên bản ứng dụng đến các tuyến đường từ một tệp khác?


103

Tôi muốn chia các tuyến đường của mình thành các tệp khác nhau, trong đó một tệp chứa tất cả các tuyến và tệp kia chứa các hành động tương ứng. Tôi hiện có một giải pháp để đạt được điều này, tuy nhiên tôi cần đặt phiên bản ứng dụng ra toàn cầu để có thể truy cập nó trong các hành động. Thiết lập hiện tại của tôi trông như thế này:

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.

};

bộ điều khiển / index.js:

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

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

bộ điều khiển / posts.js:

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

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

Tuy nhiên, thiết lập này có một vấn đề lớn: Tôi có một cơ sở dữ liệu- và một phiên bản ứng dụng mà tôi cần chuyển cho các hành động (bộ điều khiển / *. Js). Lựa chọn duy nhất mà tôi có thể nghĩ đến, là làm cho cả hai biến trở nên toàn cầu, đây không phải là một giải pháp. Tôi muốn tách các tuyến đường khỏi các hành động vì tôi có rất nhiều tuyến đường và muốn chúng ở vị trí trung tâm.

Cách tốt nhất để chuyển các biến cho các hành động nhưng tách các hành động khỏi các tuyến là gì?


Controllers.js của bạn trông như thế nào? Có thể bạn có thể biến nó thành một hàm (thay vì một đối tượng) có thể nhận các tham số.
mihai

request ('bộ điều khiển') yêu cầu bộ điều khiển / index.js. Tuy nhiên, một hàm sẽ không hoạt động vì tôi sử dụng đối tượng trong các tuyến đường (xem các tuyến đường.js) và do đó không thể chuyển các đối số cho nó, ngay cả khi đó là một hàm.
Claudio Albertin

Câu trả lời:


165

Sử dụng req.app,req.app.get('somekey')

Biến ứng dụng được tạo bằng cách gọi express()được đặt trên các đối tượng yêu cầu và phản hồi.

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


Cảm ơn. Tôi nghĩ đây là cách tốt nhất để truy cập các biến được đặt bằng app.set ('name', val);
Pavel Kostenko,

4
Đừng quên gọi app.set('somekey', {})trong app.js
ankitjaininfo

3
Ý kiến ​​duy nhất của tôi về cách này mặc dù tôi rất thích đó là khi bạn đang cố chạy một app.locals.authorized như vậy (không phải trong main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});không thể thực hiện được vì nó nằm ngoài phạm vi yêu cầu.
gabeio

Tôi đang sử dụng chiến lược hộ chiếu khác nhau cho tham số truy vấn khác nhau. Vì vậy, tôi đang cố gắng đặt passport.use ("tên-chiến lược") trong một phần mềm trung gian. Ngay cả khi tôi lưu trữ hộ chiếu trong phần mềm trung gian đó chỉ với let passport = req.app, get ('passport'). Nó đang được sửa đổi cho một tập hợp các yêu cầu khác. Tại sao nó như vậy ?
Kartikeya Mishra

Nếu tôi làm điều này, thì đối tượng req sẽ có thêm các đối tượng Đối tượng như redis và db trong trường hợp của tôi. Nó sẽ ảnh hưởng đến hiệu suất ứng dụng? ví dụ: trong index.js app.set ('redis', redis_client); trong các tuyến đường / example.js bộ định tuyến = request ('express'). Router (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); return res.send ("// done");})
Suz Aann shrestha

101

Node.js hỗ trợ các phụ thuộc vòng tròn.
Việc sử dụng các phụ thuộc vòng tròn thay vì yêu cầu ('./ lines') (ứng dụng) làm sạch rất nhiều mã và làm cho mỗi mô-đun ít phụ thuộc lẫn nhau hơn vào tệp tải của nó:


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


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


----- Cập nhật 04/2014 -----
Express 4.0 đã sửa lỗi usecase để xác định các tuyến đường bằng cách thêm phương thức express.router ()!
tài liệu - http://expressjs.com/4x/api.html#router

Ví dụ từ trình tạo mới của họ:
Viết lộ trình:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js
Thêm / đặt tên nó vào ứng dụng: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

Vẫn còn các tiện ích để truy cập ứng dụng từ các tài nguyên khác, do đó, các phụ thuộc vòng tròn vẫn là một giải pháp hợp lệ.


1
"ít phụ thuộc lẫn nhau hơn vào tệp đang tải" - nó phụ thuộc vào đường dẫn tệp cụ thể của tệp đang tải. Đó là khớp nối rất chặt chẽ, vì vậy chúng ta đừng giả vờ là không.
Camilo Martin

2
Chỉ cần rất cẩn thận (đọc: không làm những gì tôi đã đấu tranh trong giờ qua +) mà app.jsbạn yêu cầu tệp định tuyến sau khi xuất ứng dụng. Các require()cuộc gọi vòng tròn có thể tạo ra một mớ hỗn độn thực sự, vì vậy hãy đảm bảo bạn biết chúng hoạt động như thế nào !
Nateowami

Tôi thành thật nghĩ rằng câu trả lời từ @Feng về việc sử dụng req.app.get ('somekey') thực sự là một giải pháp tốt hơn và sạch hơn so với việc sử dụng phụ thuộc tuần hoàn.
Claudio Mezzasalma

@Green nếu ứng dụng trống, bạn yêu cầu tệp yêu cầu appTRƯỚC KHI module.exportsxác định ứng dụng . Bạn phải khởi tạo app, thiết lập module.exports, sau đó yêu cầu các tệp có thể yêu cầu app Nhưng dù theo cách nào đi nữa, thực hiện các phụ thuộc vòng tròn là một phản mẫu mà express đã được giải quyết - bạn không cần phải làm điều đó nữa.
Will Stern

26

Giống như tôi đã nói trong phần bình luận, bạn có thể sử dụng một hàm dưới dạng module.exports. Một hàm cũng là một đối tượng, vì vậy bạn không cần phải thay đổi cú pháp của mình.

app.js

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

controllers.js

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

bộ điều khiển / index.js

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

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

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

module.exports = controllers;

Có thể trả về một đối tượng bên trong hàm hay tốt hơn là đặt các phương thức theo cách bạn làm trong ví dụ của mình?
Claudio Albertin

tôi nghĩ rằng một trong hai cách tiếp cận là ok.
mihai

Bởi vì tôi có rất nhiều phương thức, tôi muốn đặt chúng thành một đối tượng thay vì mỗi phương thức theo cách thủ công. Điều này sẽ hoạt động khi tôi chỉ trả về đối tượng, nhưng không có giải pháp nào tốt hơn một chút? Phương pháp thực tế của tôi sẽ được thụt vào hai lần ...
Claudio Albertin

Không chắc chắn nếu tôi hiểu bạn, nhưng tôi đoán bạn có thể di chuyển bên ngoài thực hiện mà controllerschức năng, một cái gì đó như: jsfiddle.net/mihaifm/yV79K
Mihai

không bộ điều khiển / index.js cần trả về bộ điều khiển var?
Yalamber

5

Hoặc chỉ cần làm điều đó:

var app = req.app

bên trong Middleware mà bạn đang sử dụng cho các tuyến đường này. Như vậy:

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

Ai đó cho tôi biết tại sao đây không phải là câu trả lời được chấp nhận? Đối với các phần phụ thuộc bạn sử dụng app.use('my-service', serviceInstance)trong bộ định tuyến chính và req.app.get('my-service')trong bộ điều khiển như được đề cập bởi @Feng
Felipe

0

Giả sử bạn có một thư mục tên là "contollers".

Trong app.js của bạn, bạn có thể đặt mã này:

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

... và ...

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

trong bộ điều khiển cũ hơn, bạn sẽ có tệp "ping.js" với mã này:

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

Và đây là nó....


0
  1. Để làm cho đối tượng db của bạn có thể truy cập được đối với tất cả các bộ điều khiển mà không cần chuyển nó đi khắp nơi: hãy tạo một phần mềm trung gian cấp ứng dụng để gắn đối tượng db vào mọi đối tượng req, sau đó bạn có thể truy cập nó trong mọi bộ điều khiển.
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. để tránh chuyển phiên bản ứng dụng ở khắp mọi nơi, thay vào đó, chuyển các tuyến đường đến vị trí của ứng dụng
// 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 cuối cùng:

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

Một phiên bản khác: bạn có thể tùy chỉnh điều này theo nhu cầu của riêng mình, chẳng hạn như thêm yêu cầu bài đăng

// 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

Đối với cơ sở dữ liệu, hãy tách riêng Dịch vụ truy cập dữ liệu sẽ thực hiện tất cả DB hoạt động với API đơn giản và tránh trạng thái được chia sẻ.

Tách các route.setup trông giống như overhead. Thay vào đó, tôi muốn đặt định tuyến dựa trên cấu hình. Và định cấu hình các tuyến đường bằng .json hoặc bằng chú thích.


Ý bạn là gì với Dịch vụ Truy cập Dữ liệu? Nó sẽ trông như thế nào?
Claudio Albertin

Tệp route.js thực của tôi lớn hơn nhiều và sử dụng mô-đun express-namespaces. Làm thế nào bạn sẽ tách các tuyến đường khỏi các hành động?
Claudio Albertin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.