Node.js / Express.js - Como funciona o app.router?

298

Antes de perguntar app.router, acho que devo explicar pelo menos o que acho que acontece quando se trabalha com middleware. Para usar o middleware, a função a ser usada é app.use(). Quando o middleware estiver sendo executado, ele chamará o próximo middleware usando next()ou fará com que ele não seja mais chamado. Isso significa que a ordem na qual eu faço minhas chamadas de middleware é importante, porque alguns middlewares dependem de outros middlewares, e alguns middlewares próximos ao final podem nem ser chamados.

Hoje eu estava trabalhando no meu aplicativo e meu servidor estava sendo executado em segundo plano. Eu queria fazer algumas alterações, atualizar minha página e ver as alterações imediatamente. Especificamente, eu estava fazendo alterações no meu layout. Não consegui fazê-lo funcionar, então procurei a Stack Overflow e encontrei esta pergunta . Diz para garantir que express.static()está embaixo require('stylus'). Mas quando eu estava olhando para o código do OP, vi que ele recebeu sua app.routerligação no final de suas chamadas de middleware e tentei descobrir por que isso acontecia.

Quando criei meu aplicativo Express.js (versão 3.0.0rc4), usei o comando express app --sessions --css styluse, no meu arquivo app.js, o código veio configurado com app.routeras chamadas acima express.static()e acima require('stylus'). Então parece que, se já vem configurado dessa maneira, deve permanecer assim.

Depois de reorganizar meu código para que eu pudesse ver minhas alterações no Stylus, fica assim:

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

Então, decidi que o primeiro passo seria descobrir por que é importante ter app.routerno meu código. Então, comentei, iniciei meu aplicativo e naveguei para /. Ele exibiu minha página de índice muito bem. Hmm, talvez tenha funcionado porque eu estava exportando o roteamento do meu arquivo de rotas (routes.index). Então, em seguida, naveguei /teste exibi Teste na tela. Haha, ok, eu não tenho ideia do que app.routerfaz. Esteja ou não incluído no meu código, meu roteamento está correto. Então, definitivamente estou perdendo alguma coisa.

Então aqui está a minha pergunta:

Alguém poderia explicar o que app.routerfaz, a importância e onde devo colocá-lo nas minhas chamadas de middleware? Também seria bom se eu tivesse uma breve explicação sobre express.static(). Pelo que sei, express.static()há um cache de minhas informações e, se o aplicativo não conseguir encontrar a página solicitada, ele verificará o cache para verificar se existe.

Aust
fonte
18
Obrigado por fazer esta pergunta. Tenho pesquisado no Google para encontrar esta resposta (e a pergunta para solicitá-la).
Hari Seldon
8
Essa foi uma pergunta muito bem escrita, eu estava pesquisando a mesma coisa no Google.
Kirn

Respostas:

329

Nota: Isso descreve como o Express funcionou nas versões 2 e 3. Consulte o final desta postagem para obter informações sobre o Express 4.


staticsimplesmente serve arquivos ( recursos estáticos ) do disco. Você indica um caminho (às vezes chamado de ponto de montagem) e ele serve os arquivos nessa pasta.

Por exemplo, express.static('/var/www')serviria os arquivos nessa pasta. Portanto, uma solicitação para o servidor Node http://server/file.htmlseria veiculada /var/www/file.html.

routeré um código que executa suas rotas. Quando você faz app.get('/user', function(req, res) { ... });isso, é o routerque realmente chama a função de retorno de chamada para processar a solicitação.

A ordem em que você passa as coisas app.usedetermina a ordem na qual cada middleware tem a oportunidade de processar uma solicitação. Por exemplo, se você tiver um arquivo chamado test.htmlna sua pasta estática e uma rota:

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

Qual deles é enviado para um cliente solicitando http://server/test.html? Qualquer que seja o middleware fornecido useprimeiro.

Se você fizer isto:

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

Em seguida, o arquivo no disco é servido.

Se você fizer o contrário,

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

Em seguida, o manipulador de rotas recebe a solicitação e "Olá do manipulador de rotas" é enviado ao navegador.

Geralmente, você deseja colocar o roteador acima do middleware estático para que um arquivo chamado acidentalmente não possa substituir uma das suas rotas.

Note que se você não fizer isso explicitamente useo router, ele é implicitamente adicionado por expresso no ponto em que definir uma rota (que é por isso que suas rotas ainda funcionava mesmo que você comentado app.use(app.router)).


Um comentarista levantou outro ponto sobre a ordem statice routerque eu não havia abordado: o impacto no desempenho geral do seu aplicativo.

Outro motivo use routeracima staticé otimizar o desempenho. Se você colocar staticprimeiro, você acessará o disco rígido em cada solicitação para verificar se existe ou não um arquivo. Em um teste rápido , descobri que essa sobrecarga era de ~ 1ms em um servidor descarregado. (É muito provável que esse número seja maior sob carga, onde as solicitações competirão pelo acesso ao disco.)

Com o routerprimeiro, uma solicitação correspondente a uma rota nunca precisa atingir o disco, economizando milissegundos preciosos.

Obviamente, existem maneiras de atenuar statica sobrecarga.

A melhor opção é colocar todos os seus recursos estáticos em uma pasta específica. (IE /static) Você pode montar staticnesse caminho para que ele seja executado somente quando o caminho iniciar com /static:

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

Nesta situação, você colocaria isso acima router. Isso evita o processamento de outro middleware / roteador se houver um arquivo, mas, para ser sincero, duvido que você ganhe muito.

Você também pode usar staticCache, que armazena em cache recursos estáticos na memória para que você não precise atingir o disco para obter os arquivos solicitados com mais freqüência. ( Aviso: staticCache aparentemente será removido no futuro.)

No entanto, não creio que staticCacheoculte respostas negativas (quando um arquivo não existe), portanto, não ajuda se você colocou staticCacheacima routersem montá-lo em um caminho.

Como em todas as perguntas sobre desempenho, meça e avalie seu aplicativo do mundo real (sob carga) para ver onde realmente estão os gargalos.


Express 4

O Express 4.0 é removido app.router . Todos os middlewares ( app.use) e rotas ( app.getet al) agora são processados ​​exatamente na ordem em que são adicionados.

Em outras palavras:

Todos os métodos de roteamento serão adicionados na ordem em que aparecem. Você não deveria fazer app.use(app.router). Isso elimina o problema mais comum do Express.

Em outras palavras, mixar app.use()e app[VERB]()funcionará exatamente na ordem em que são chamados.

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

Leia mais sobre alterações no Express 4.

josh3736
fonte
2
O routervai em um só lugar. Se, na primeira vez em que você ligar app.get(ou em postoutros), ainda não tiver used app.router, o Express o adicionará para você.
josh3736
4
@MikeCauser: Não, porque a sobrecarga do acesso ao disco (para ver se existe ou não um arquivo) é maior que a sobrecarga da chamada de função. No meu teste , essa sobrecarga foi de 1 ms em um servidor descarregado. É muito provável que seja maior sob carga, onde as solicitações competirão pelo acesso ao disco. Com o staticdepois router, a pergunta sobre o outro middleware se torna irrelevante, pois deve estar acima do roteador.
josh3736
2
Explicação maravilhosa! Muito obrigado!
Kirn
3
app.routeré removido no ramo mestre atual, que será express-4.0 . Cada rota se torna um middleware separado.
yanychar
3
Mais um esclarecimento, enquanto trabalho com isso. No express 4, várias rotas podem ser atribuídas a um roteador e, para usá-lo, ele recebe um caminho raiz e é colocado na pilha "middleware" via app.use (caminho, roteador). Isso permite que rotas relacionadas a cada um usem seu próprio roteador e que sejam atribuídos um caminho base como uma unidade. Se eu entendesse melhor, me ofereceria para postar outra resposta. Mais uma vez, eu estou obtendo isso de scotch.io/tutorials/javascript/…
Joe Lapp
2

Roteamento significa determinar como um aplicativo responde a uma solicitação do cliente para um terminal específico, que é um URI (ou caminho) e um método de solicitação HTTP específico (GET, POST e assim por diante). Cada rota pode ter uma ou mais funções de manipulador, que são executadas quando a rota é correspondida.

No Express 4.0 Router, temos mais flexibilidade do que nunca na definição de nossas rotas.

express.Router () é usado várias vezes para definir grupos de rotas.

rota usada como middleware para processar solicitações.

rota usada como middleware para validar parâmetros usando ".param ()".

app.route () usado como um atalho para o roteador para definir várias solicitações em uma rota

quando estamos usando app.route (), estamos anexando nosso aplicativo a esse roteador.

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
fonte
0

Na versão expressa 4, podemos facilmente definir rotas da seguinte maneira:

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;

Em server.jsnós importamos o objeto roteador do route.jsarquivo e o aplicamos da seguinte maneira em server.js:

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

Agora, todas as rotas no route.jstêm o seguinte URL base:

http: // localhost: 3000 / route

Por que essa abordagem:

A principal vantagem de adotar essa abordagem é que agora nosso aplicativo é mais modular . Todos os manipuladores de rota para uma determinada rota agora podem ser colocados em arquivos diferentes, o que torna tudo mais sustentável e fácil de encontrar.

Willem van der Veen
fonte