Node.js / Express.js - Comment fonctionne app.router?

298

Avant de poser des questions, app.routerje pense que je devrais expliquer au moins ce qui se passe lorsque je travaille avec un middleware. Pour utiliser le middleware, la fonction à utiliser est app.use(). Lorsque le middleware est en cours d'exécution, il appellera le prochain middleware à l'aide de next()ou fera en sorte qu'aucun middleware ne soit appelé. Cela signifie que l'ordre dans lequel je passe mes appels de middleware est important, car certains middleware dépendent d'autres middleware, et certains middleware proches de la fin peuvent même ne pas être appelés.

Aujourd'hui, je travaillais sur mon application et mon serveur fonctionnait en arrière-plan. Je voulais faire quelques changements et rafraîchir ma page et voir les changements immédiatement. Plus précisément, je modifiais ma disposition. Je n'ai pas réussi à le faire fonctionner, j'ai donc cherché dans Stack Overflow la réponse et trouvé cette question . Il dit de s'assurer que express.static()c'est en dessous require('stylus'). Mais quand j'ai regardé le code de ce PO, j'ai vu qu'il avait son app.routerappel à la toute fin de ses appels de middleware, et j'ai essayé de comprendre pourquoi.

Quand je fait ma demande express.js (version 3.0.0rc4), je la commande express app --sessions --css styluset dans mon fichier app.js le code venu configuration avec mon app.routerci - dessus à la fois le express.static()et les require('stylus')appels. Il semble donc que, s'il est déjà configuré de cette façon, il devrait le rester.

Après avoir réorganisé mon code afin que je puisse voir mes modifications de stylet, il ressemble à ceci:

app.configure(function(){
  //app.set() calls
  //app.use() calls
  //...
  app.use(app.router);
  app.use(require('stylus').middleware(__dirname + '/public'));
  app.use(express.static(__dirname + '/public', {maxAge: 31557600000}));
});

app.get('/', routes.index);

app.get('/test', function(req, res){
  res.send('Test');
});

J'ai donc décidé que la première étape serait de découvrir pourquoi il est important d'avoir même app.routerdans mon code. J'ai donc commenté, lancé mon application et accédé à /. Il a très bien affiché ma page d'index. Hmm, cela a peut-être fonctionné parce que j'exportais le routage à partir de mon fichier de routes (routes.index). J'ai ensuite navigué vers /testet il a affiché Test sur l'écran. Haha, OK, je ne sais pas ce que app.routerça fait. Qu'il soit inclus dans mon code ou non, mon routage est correct. Il me manque donc quelque chose.

Voici donc ma question:

Quelqu'un pourrait-il expliquer ce qu'il app.routerfait, son importance et où je devrais le placer dans mes appels middleware? Ce serait également bien si j'obtenais une brève explication express.static(). Autant que je sache, il express.static()s'agit d'un cache de mes informations, et si l'application ne trouve pas la page demandée, elle vérifiera le cache pour voir si elle existe.

Aust
la source
18
Merci d'avoir posé cette question. J'ai cherché sur Google pour trouver cette réponse (et la question pour y répondre).
Hari Seldon
8
C'était une question vraiment bien écrite, je cherchais la même chose sur Google.
Kirn

Réponses:

329

Remarque: cela décrit le fonctionnement d'Express dans les versions 2 et 3. Voir la fin de ce post pour plus d'informations sur Express 4.


staticsert simplement des fichiers ( ressources statiques ) à partir du disque. Vous lui donnez un chemin (parfois appelé point de montage) et il sert les fichiers de ce dossier.

Par exemple, express.static('/var/www')servirait les fichiers dans ce dossier. Une requête à votre serveur Node http://server/file.htmlpourrait donc être utilisée /var/www/file.html.

routerest le code qui exécute vos itinéraires. Lorsque vous le faites app.get('/user', function(req, res) { ... });, c'est le routerqui appelle réellement la fonction de rappel pour traiter la demande.

L'ordre dans lequel vous passez les éléments app.usedétermine l'ordre dans lequel chaque middleware a la possibilité de traiter une demande. Par exemple, si vous avez un fichier appelé test.htmldans votre dossier statique et un itinéraire:

app.get('/test.html', function(req, res) {
    res.send('Hello from route handler');
});

Lequel est envoyé à un client qui le demande http://server/test.html? Quel que soit le middleware attribué en usepremier.

Si tu fais ça:

app.use(express.static(__dirname + '/public'));
app.use(app.router);

Ensuite, le fichier sur le disque est servi.

Si vous le faites dans l'autre sens,

app.use(app.router);
app.use(express.static(__dirname + '/public'));

Ensuite, le gestionnaire d'itinéraire reçoit la demande et "Bonjour du gestionnaire d'itinéraire" est envoyé au navigateur.

Habituellement, vous souhaitez placer le routeur au - dessus du middleware statique afin qu'un fichier nommé accidentellement ne puisse pas remplacer l'un de vos itinéraires.

Notez que si vous ne le faites pas explicitement usela router, il est implicitement ajouté par exprès au point que vous définissez un itinéraire ( ce qui explique pourquoi vos itinéraires toujours travaillé , même si vous en commentaires app.use(app.router)).


Un intervenant a soulevé un autre point à propos de l'ordre de staticet routerque je n'avais pas abordé: l'impact sur les performances globales de votre application.

Une autre raison use routerci static- dessus est d'optimiser les performances. Si vous mettez en staticpremier, vous frapperez le disque dur à chaque demande pour voir si un fichier existe ou non. Dans un test rapide , j'ai constaté que cette surcharge s'élevait à ~ 1 ms sur un serveur déchargé. (Ce nombre est susceptible d'être plus élevé sous charge, où les demandes entreront en concurrence pour l'accès au disque.)

Avec d' routerabord, une requête correspondant à un itinéraire n'a jamais à frapper le disque, ce qui permet d'économiser de précieuses millisecondes.

Bien sûr, il existe des moyens d'atténuer staticles frais généraux de.

La meilleure option consiste à placer toutes vos ressources statiques dans un dossier spécifique. (IE /static) Vous pouvez ensuite monter staticsur ce chemin afin qu'il ne s'exécute que lorsque le chemin commence par /static:

app.use('/static', express.static(__dirname + '/static'));

Dans cette situation, vous mettriez cela ci-dessus router. Cela évite de traiter d'autres middlewares / le routeur si un fichier est présent, mais pour être honnête, je doute que vous gagniez autant.

Vous pouvez également utiliser staticCache, qui met en cache les ressources statiques en mémoire afin que vous n'ayez pas à frapper le disque pour les fichiers fréquemment demandés. ( Avertissement: staticCache sera apparemment supprimé à l'avenir.)

Cependant, je ne pense pas que staticCacheles réponses négatives en cache (quand un fichier n'existe pas), donc cela n'aide pas si vous avez mis staticCacheci-dessus routersans le monter sur un chemin.

Comme pour toutes les questions sur les performances, mesurez et comparez votre application réelle (sous charge) pour voir où se trouvent réellement les goulots d'étranglement.


Express 4

Express 4.0 supprime app.router . Tous les middleware ( app.use) et routes ( app.getet al) sont désormais traités dans l'ordre précis dans lequel ils sont ajoutés.

En d'autres termes:

Toutes les méthodes de routage seront ajoutées dans l'ordre dans lequel elles apparaissent. Tu ne devrais pas faire app.use(app.router). Cela élimine le problème le plus courant avec Express.

En d'autres termes, le mélange app.use()et app[VERB]()fonctionnera exactement dans l'ordre dans lequel ils sont appelés.

app.get('/', home);
app.use('/public', require('st')(process.cwd()));
app.get('/users', users.list);
app.post('/users', users.create);

En savoir plus sur les modifications dans Express 4.

josh3736
la source
2
Le routerva en un seul endroit. Si, la première fois que vous appelez app.get(ou postou autres), vous ne l' avez pas encore used app.router, Express , il vous ajoute.
josh3736
4
@MikeCauser: Non, car la surcharge d'accès au disque (pour voir si un fichier existe ou non) est supérieure à la surcharge de l'appel de fonction. Dans mon test , cette surcharge s'élevait à 1 ms sur un serveur déchargé. C'est probablement plus élevé sous charge, où les demandes seront en concurrence pour l'accès au disque. Avec staticAfter router, la question sur les autres middlewares devient sans objet puisqu'elle doit être au dessus du routeur.
josh3736
2
Magnifique explication! Merci beaucoup!
Kirn
3
app.routerest supprimé dans la branche principale actuelle, qui sera express-4.0 . Chaque route devient un middleware distinct.
yanychar
3
Une précision supplémentaire, car je travaille avec cela. Dans express 4, plusieurs routes peuvent être attribuées à un routeur, puis pour utiliser le routeur, le routeur reçoit un chemin racine et est placé dans la pile "middleware" via app.use (chemin, routeur). Cela permet aux itinéraires associés d'utiliser chacun leur propre routeur et de se voir attribuer un chemin de base en tant qu'unité. Si je l'ai mieux compris, je proposerais de poster une autre réponse. Encore une fois, je reçois cela de scotch.io/tutorials/javascript/…
Joe Lapp
2

Le routage signifie déterminer comment une application répond à une demande client à un point de terminaison particulier, qui est un URI (ou chemin) et une méthode de demande HTTP spécifique (GET, POST, etc.). Chaque route peut avoir une ou plusieurs fonctions de gestionnaire, qui sont exécutées lorsque la route est mise en correspondance.

Dans Express 4.0 Router, nous avons plus de flexibilité que jamais pour définir nos itinéraires.

express.Router () est utilisé plusieurs fois pour définir des groupes de routes.

route utilisée comme middleware pour traiter les requêtes.

route utilisée comme middleware pour valider les paramètres à l'aide de ".param ()".

app.route () utilisé comme raccourci vers le routeur pour définir plusieurs requêtes sur un itinéraire

lorsque nous utilisons app.route (), nous attachons notre application à ce routeur.

var express = require('express'); //used as middleware
var app = express(); //instance of express.
app.use(app.router);
app.use(express.static(__dirname + '/public')); //All Static like [css,js,images] files are coming from public folder
app.set('views',__dirname + '/views'); //To set Views
app.set('view engine', 'ejs'); //sets View-Engine as ejs
app.engine('html', require('ejs').renderFile); //actually rendering HTML files through EJS. 
app.get('/', function (req, res) {
  res.render('index');  
})
app.get('/test', function (req, res) {
  res.send('test')
})
Parth Raval
la source
0

Dans la version express 4, nous pouvons facilement définir des itinéraires de la manière suivante:

server.js:

const express = require('express');
const app = express();
const route = require('./route');

app.use('/route', route);
// here we pass in the imported route object

app.listen(3000, () => console.log('Example app listening on port 3000!'));

route.js:

const express = require('express');
const router = express.Router();

router.get('/specialRoute', function (req, res, next) {
     // route is now http://localhost:3000/route/specialRoute
});

router.get('/', function (req, res, next) {
    // route is now http://localhost:3000/route
});

module.exports = router;

Dans, server.jsnous avons importé l'objet routeur du route.jsfichier et l' avons appliqué de la manière suivante server.js:

app.use('/route', route);

Maintenant, tous les itinéraires du route.jsont l'URL de base suivante:

http: // localhost: 3000 / route

Pourquoi cette approche:

Le principal avantage de cette approche est que notre application est désormais plus modulaire . Tous les gestionnaires de route pour une certaine route peuvent maintenant être placés dans des fichiers différents, ce qui rend tout plus facile à gérer et à trouver.

Willem van der Veen
la source