Express: จะส่งแอปอินสแตนซ์ไปยังเส้นทางจากไฟล์อื่นได้อย่างไร


104

ฉันต้องการแยกเส้นทางของฉันออกเป็นไฟล์ต่างๆโดยที่ไฟล์หนึ่งมีเส้นทางทั้งหมดและอีกไฟล์หนึ่งเป็นการดำเนินการที่เกี่ยวข้อง ขณะนี้ฉันมีวิธีแก้ปัญหาเพื่อให้บรรลุสิ่งนี้อย่างไรก็ตามฉันต้องทำให้อินสแตนซ์แอปเป็นสากลเพื่อให้สามารถเข้าถึงได้ในการดำเนินการ การตั้งค่าปัจจุบันของฉันมีลักษณะดังนี้:

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

เส้นทาง js:

exports.setup = function(app, controllers) {

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

};

ตัวควบคุม / index.js:

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

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

ตัวควบคุม / posts.js:

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

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

อย่างไรก็ตามการตั้งค่านี้มีปัญหาใหญ่: ฉันมีฐานข้อมูลและแอปอินสแตนซ์ที่ฉันต้องการส่งต่อไปยังการดำเนินการ (ตัวควบคุม / *. js) ทางเลือกเดียวที่ฉันคิดได้คือการสร้างตัวแปรทั้งสองให้เป็นสากลซึ่งไม่ใช่วิธีแก้ปัญหาจริงๆ ฉันต้องการแยกเส้นทางออกจากการดำเนินการเนื่องจากฉันมีเส้นทางจำนวนมากและต้องการให้อยู่ในจุดศูนย์กลาง

วิธีใดเป็นวิธีที่ดีที่สุดในการส่งผ่านตัวแปรไปยังการดำเนินการ แต่แยกการดำเนินการออกจากเส้นทาง


controllers.js ของคุณมีลักษณะอย่างไร บางทีคุณอาจทำให้เป็นฟังก์ชัน (แทนที่จะเป็นวัตถุ) ที่สามารถรับพารามิเตอร์ได้
mihai

ต้องใช้ ('คอนโทรลเลอร์') ต้องการคอนโทรลเลอร์ / index.js อย่างไรก็ตามฟังก์ชันจะไม่ทำงานเนื่องจากฉันใช้ออบเจ็กต์ในเส้นทาง (ดู route.js) จึงไม่สามารถส่งผ่านอาร์กิวเมนต์ไปได้แม้ว่าจะเป็นฟังก์ชันก็ตาม
Claudio Albertin

คำตอบ:


167

การใช้งานreq.app,req.app.get('somekey')

ตัวแปรแอ็พพลิเคชันที่สร้างโดยการเรียกexpress()ถูกตั้งค่าบนอ็อบเจ็กต์การร้องขอและการตอบสนอง

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


ขอบคุณ. ฉันคิดว่านี่เป็นวิธีที่ดีที่สุดในการเข้าถึงตัวแปรที่ตั้งค่าด้วย app.set ('name', val);
Pavel Kostenko

4
อย่าลืมโทรapp.set('somekey', {})ใน app.js
ankitjaininfo

3
สิ่งเดียวที่ฉันจับได้เกี่ยวกับวิธีนี้แม้ว่าฉันจะชอบก็คือเมื่อคุณพยายามเรียกใช้ app.locals.authorized เช่นนี้ (ไม่ใช่ใน main.js): app.route('/something').get(app.locals.authorized,function(req,res,next){});เป็นไปไม่ได้เพราะอยู่นอกขอบเขตการร้องขอ
gabeio

ฉันใช้กลยุทธ์หนังสือเดินทางที่แตกต่างกันสำหรับพารามิเตอร์การค้นหาที่แตกต่างกัน ดังนั้นฉันจึงพยายามตั้งค่า passport.use ("ชื่อกลยุทธ์") ในมิดเดิลแวร์ แม้ว่าฉันจะเก็บหนังสือเดินทางไว้ในมิดเดิลแวร์นั้นโดยใช้ let passport = req.app รับ ('passport') เท่านั้น กำลังแก้ไขสำหรับคำขอชุดอื่น ทำไมจึงเป็นเช่นนั้น?
Kartikeya Mishra

ถ้าฉันทำสิ่งนี้ req object จะมีอินสแตนซ์ Object เพิ่มเติมเช่น redis และ db ในกรณีของฉัน จะไม่ส่งผลต่อประสิทธิภาพของแอปพลิเคชันหรือไม่? เช่นใน index.js app.set ('redis', redis_client); ในเส้นทาง / example.js router = ต้องใช้ ('express') เราเตอร์ (); route.get ('/ test', (req, res, next) => {conosle.log (req.app.get ('redis')); ส่งคืน res.send ("// done");})
Suz Aann shrestha

102

Node.js รองรับการอ้างอิงแบบวงกลม
การใช้การอ้างอิงแบบวงกลมแทนที่จะต้องใช้ ('./ route') (app) ล้างโค้ดจำนวนมากและทำให้แต่ละโมดูลไม่พึ่งพากันในไฟล์โหลด:


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


เส้นทาง / 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 -----
Express 4.0 แก้ไข usecase สำหรับการกำหนดเส้นทางโดยการเพิ่มเมธอด express.router ()!
เอกสารประกอบ - http://expressjs.com/4x/api.html#router

ตัวอย่างจากตัวสร้างใหม่:
การเขียนเส้นทาง:
https://github.com/expressjs/generator/blob/master/templates/js/routes/index.js การ
เพิ่ม / namespacing ไปยังแอป: https://github.com /expressjs/generator/blob/master/templates/js/app.js#L24

ยังคงมีกรณีการใช้งานสำหรับการเข้าถึงแอปจากแหล่งข้อมูลอื่น ๆ ดังนั้นการอ้างอิงแบบวงกลมจึงยังคงเป็นทางออกที่ถูกต้อง


1
"พึ่งพาซึ่งกันและกันน้อยกว่าในการโหลดไฟล์" - ขึ้นอยู่กับfilepathเฉพาะของไฟล์ที่กำลังโหลด การมีเพศสัมพันธ์ที่แน่นมากดังนั้นอย่าแสร้งทำเป็นว่าไม่ใช่
Camilo Martin

2
ระวังให้มาก (อ่าน: อย่าทำในสิ่งที่ฉันดิ้นรนมาตลอดชั่วโมงที่ผ่านมา +) ซึ่งapp.jsคุณต้องใช้ไฟล์เส้นทางหลังจากส่งออกแอป การrequire()โทรแบบวนซ้ำสามารถทำให้เกิดความยุ่งเหยิงได้ดังนั้นอย่าลืมรู้ว่ามันทำงานอย่างไร !
Nateowami

ฉันคิดตามตรงว่าคำตอบจาก @Feng เกี่ยวกับการใช้ req.app.get ('somekey') เป็นวิธีที่ดีและสะอาดกว่าการใช้การอ้างอิงแบบ circulr
Claudio Mezzasalma

@ กรีนหากแอปว่างคุณต้องมีไฟล์ที่ต้องใช้appก่อนmodule.exportsกำหนดแอป คุณต้องสร้างอินสแตนซ์appตั้งค่าmodule.exportsแล้วต้องใช้ไฟล์ที่อาจต้องการapp แต่ไม่ว่าจะด้วยวิธีใดการพึ่งพาแบบวงกลมเป็นการต่อต้านรูปแบบที่ด่วนได้รับการแก้ไข - คุณไม่จำเป็นต้องทำเช่นนั้นอีกต่อไป
Will Stern

26

อย่างที่ฉันพูดในความคิดเห็นคุณสามารถใช้ฟังก์ชันเป็น module.exports ฟังก์ชันก็เป็นวัตถุเช่นกันดังนั้นคุณไม่จำเป็นต้องเปลี่ยนไวยากรณ์ของคุณ

app.js

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

controllers.js

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

คอนโทรลเลอร์ / index.js

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

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

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

module.exports = controllers;

สามารถส่งคืนวัตถุภายในฟังก์ชันได้หรือดีกว่าดังนั้นให้กำหนดวิธีการตามที่คุณทำในตัวอย่างของคุณ
Claudio Albertin

ฉันคิดว่าแนวทางใดก็โอเค
mihai

เนื่องจากฉันมีวิธีการมากมายฉันจึงต้องการตั้งค่าเป็นวัตถุแทนการตั้งค่าด้วยตนเอง สิ่งนี้จะได้ผลเมื่อฉันส่งคืนวัตถุ แต่ไม่มีวิธีแก้ปัญหาที่ค่อนข้างประจบ? วิธีการจริงของฉันจะเยื้องสองครั้ง ...
Claudio Albertin

ไม่แน่ใจว่าฉันเข้าใจคุณหรือเปล่าแต่ฉันเดาว่าคุณสามารถย้ายการใช้งานนอกcontrollersฟังก์ชันนั้นได้เช่น: jsfiddle.net/mihaifm/yV79K
mihai

คอนโทรลเลอร์ / ดัชนี js ไม่จำเป็นต้องส่งคืนคอนโทรลเลอร์ var หรือไม่
Yalamber

5

หรือเพียงแค่ทำ:

var app = req.app

ภายในมิดเดิลแวร์ที่คุณใช้สำหรับเส้นทางเหล่านี้ เช่นนั้น:

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

มีคนบอกฉันว่าทำไมนี่ไม่ใช่คำตอบที่ยอมรับ? สำหรับการอ้างอิงที่คุณใช้app.use('my-service', serviceInstance)ในเราเตอร์หลักและreq.app.get('my-service')ในคอนโทรลเลอร์ตามที่กล่าวไว้โดย @Feng
Felipe

0

สมมติว่าคุณมีโฟลเดอร์ชื่อ "contollers"

ใน app.js ของคุณคุณสามารถใส่รหัสนี้:

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

... และ ...

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

ในตัวควบคุมของคุณ forlder คุณจะมีไฟล์ "ping.js" พร้อมรหัสนี้:

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

และนี่มัน ....


0
  1. ในการทำให้อ็อบเจ็กต์ db ของคุณสามารถเข้าถึงคอนโทรลเลอร์ทั้งหมดได้โดยไม่ต้องส่งผ่านไปทุกที่: สร้างมิดเดิลแวร์ระดับแอพพลิเคชั่นที่แนบ db อ็อบเจ็กต์เข้ากับอ็อบเจ็กต์ req ทุกตัวจากนั้นคุณสามารถเข้าถึงอ็อบเจ็กต์ภายในในคอนโทรลเลอร์ทุกตัว
// app.js
let db = ...;  // your db object initialized
const contextMiddleware = (req, res, next) => {
  req.db=db;
  next();
};
app.use(contextMiddleware);
  1. เพื่อหลีกเลี่ยงการส่งผ่านอินสแตนซ์ของแอปทุกที่แทนที่จะส่งเส้นทางไปยังที่ที่แอปอยู่
// 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 สุดท้าย:

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

เวอร์ชันอื่น: คุณสามารถปรับแต่งได้ตามความต้องการของคุณเองเช่นการเพิ่มคำขอโพสต์

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

0

หากคุณต้องการส่งผ่านแอพอินสแตนซ์ไปยังผู้อื่นในNode-typescript :

ตัวเลือกที่ 1: ด้วยความช่วยเหลือของimport( เมื่อนำเข้า )

//routes.ts
import { Application } from "express";
import { categoryRoute } from './routes/admin/category.route'
import { courseRoute } from './routes/admin/course.route';

const routing = (app: Application) => {
    app.use('/api/admin/category', categoryRoute)
    app.use('/api/admin/course', courseRoute)
}
export { routing }

จากนั้นนำเข้าและส่งผ่านแอป:

import express, { Application } from 'express';

const app: Application = express();
import('./routes').then(m => m.routing(app))

ตัวเลือกที่ 2 : ด้วยความช่วยเหลือของclass

// index.ts
import express, { Application } from 'express';
import { Routes } from './routes';


const app: Application = express();
const rotues = new Routes(app)
...

ที่นี่เราจะเข้าถึงแอปในตัวสร้างของคลาสเส้นทาง

// routes.ts
import { Application } from 'express'
import { categoryRoute } from '../routes/admin/category.route'
import { courseRoute } from '../routes/admin/course.route';

class Routes {
    constructor(private app: Application) {
        this.apply();
    }

    private apply(): void {
       this.app.use('/api/admin/category', categoryRoute)
       this.app.use('/api/admin/course', courseRoute)
    }
}

export { Routes }

-1

สำหรับฐานข้อมูลแยกออกจาก Data Access Service ซึ่งจะทำให้ DB ทั้งหมดทำงานกับ API แบบง่ายและหลีกเลี่ยงสถานะที่ใช้ร่วมกัน

การแยก route.setup ดูเหมือนค่าใช้จ่าย ฉันต้องการวางการกำหนดเส้นทางตามการกำหนดค่าแทน และกำหนดค่าเส้นทางใน. json หรือด้วยคำอธิบายประกอบ


คุณหมายถึงอะไรกับบริการการเข้าถึงข้อมูล? หน้าตาจะเป็นยังไง?
Claudio Albertin

ไฟล์ route.js จริงของฉันใหญ่กว่ามากและใช้โมดูล express-namespaces คุณจะแยกเส้นทางออกจากการกระทำอย่างไร?
Claudio Albertin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.