Node.js / Express.js - app.router如何工作?

在我问app.router之前,我想我至少应该解释一下我认为在使用中间件时发生了什么。要使用中间件,要使用的函数是app.use()。当中间件被执行时,它将使用next()调用下一个中间件,或者使它不再被调用。这意味着我放置中间件调用的顺序很重要,因为一些中间件依赖于其他中间件,而靠近末尾的一些中间件甚至可能不被调用。

今天我正在开发我的应用程序,并让服务器在后台运行。我想做一些更改,刷新我的页面,并立即看到更改。具体来说,我是在改变我的布局。我不能让它工作,所以我搜索Stack Overflow的答案,并找到这个问题。它说要确保express.static()require('stylus')下面。但是当我查看那个OP的代码时,我看到他在中间件调用的最后有app.router调用,我试图弄清楚为什么会这样。

当我创建Express.js应用程序(3.0.0rc4版本)时,我使用命令express app --sessions --css stylus,在我的app.js文件中,代码与我的app.router一起设置在express.static()require('stylus')调用之上。所以看起来,如果它已经这样设置了,那么它应该保持这样。

在重新安排我的代码,这样我就可以看到我的手写笔的变化,它看起来像这样:

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

所以我决定第一步是找出为什么在我的代码中有app.router是重要的。所以我把它注释掉,启动我的应用程序并导航到/。它很好地显示了我的索引页。嗯,也许它工作,因为我从我的路由文件(routes.index)导出路由。所以接下来我导航到/test,它在屏幕上显示Test。哈哈,好吧,我不知道app.router是做什么的。无论它是否包含在我的代码中,我的路由都很好。所以我肯定遗漏了一些东西。

所以我的问题是:

有人能解释一下app.router做什么,它的重要性,以及我应该把它放在中间件调用的地方吗?如果我能得到一个关于express.static()的简短解释,那就太好了。据我所知,express.static()是我信息的缓存,如果应用程序无法找到所请求的页面,它将检查缓存,看看它是否存在。

83995 次浏览

注意:描述Express在版本2和3中的工作方式。有关特快4的信息,请参阅本文末尾。


static只是从磁盘上提供文件(静态资源)。您给它一个路径(有时称为挂载点),它为该文件夹中的文件提供服务。

例如,express.static('/var/www')将提供该文件夹中的文件。因此,向Node服务器请求http://server/file.html将提供/var/www/file.html

router是运行你的路由的代码。当你执行app.get('/user', function(req, res) { ... });时,实际上是router调用回调函数来处理请求。

传递给app.use的顺序决定了每个中间件有机会处理请求的顺序。例如,如果你的静态文件夹中有一个名为test.html的文件和一个路由:

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

哪一个被发送到请求http://server/test.html的客户端?无论哪个中间件首先被赋予use

如果你这样做:

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

然后提供磁盘上的文件。

如果你用另一种方法,

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

然后路由处理程序得到请求,“Hello from route handler”;被发送到浏览器。

通常,你想把路由器以上放在静态中间件中,这样一个意外命名的文件就不能覆盖你的路由了。

注意,如果你没有显式地use router,它会在你定义路由时被Express隐式添加(这就是为什么即使你注释掉了app.use(app.router),你的路由仍然可以工作)。


一个评论者有另一个关于staticrouter顺序的观点,我还没有解决:对应用程序整体性能的影响。

use router置于static之上的另一个原因是为了优化性能。如果你把static放在前面,那么你将在每次请求时都敲击硬盘驱动器,看看是否有文件存在。在快速测试中,我发现这个开销在一个未加载的服务器上达到了~1ms。(在负载情况下,这个数字很可能更高,因为请求将争夺磁盘访问。)

首先使用router,匹配路由的请求永远不必到达磁盘,从而节省宝贵的毫秒。

当然,有一些方法可以减少static的开销。

最好的选择是把你所有的静态资源放在一个特定的文件夹下。然后你可以将static挂载到该路径,这样它只在路径以/static开头时运行:

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

在这种情况下,你会把它放在router上面。如果文件存在,这避免了处理其他中间件/路由器,但说实话,我怀疑你能得到那么多。

你也可以使用staticCache,它在内存中缓存静态资源,这样你就不必为常见的请求文件去磁盘了。(将来使用警告: staticCache 显然会被移除。)

然而,我不认为staticCache缓存否定答案(当文件不存在时),所以如果你把staticCache放在router上面而没有挂载到一个路径上,它是没有帮助的。

与所有关于性能的问题一样,衡量和基准测试现实世界的应用程序(负载下)来查看瓶颈真正在哪里。


表达4

Express 4.0 删除 app.router。所有中间件(app.use)和路由(app.get等)现在都按照它们被添加的顺序被精确地处理。

换句话说:

所有路由方法将按照它们出现的顺序添加。你应该app.use(app.router)。这消除了Express最常见的问题。

换句话说,混合app.use()app[VERB]()将按照它们被调用的顺序工作。

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

阅读更多关于Express 4的变化

路由意味着确定应用程序如何响应客户端对特定端点的请求,该端点是一个URI(或路径)和一个特定的HTTP请求方法(GET、POST等)。 每条路由可以有一个或多个处理函数,当路由被匹配时执行

在Express 4.0路由器中,我们在定义路由时获得了比以往任何时候都更大的灵活性。

express.Router()被多次使用来定义路由组。

路由用作处理请求的中间件。

使用“.param()”作为中间件验证参数的路由。

app.route()用作路由器的快捷方式,在路由上定义多个请求

当我们使用app.route()时,我们将应用程序与该路由器连接在一起。

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

在express版本4中,我们可以很容易地以以下方式定义路由:

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;

server.js中,我们导入了route.js文件的路由器对象,并在server.js中以以下方式应用它:

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

现在route.js中的所有路由都有以下基本URL:

http://localhost:3000/route

为什么采用这种方法:

采用这种方法的主要好处是,现在我们的应用程序更模块化。某个路由的所有路由处理程序现在都可以放在不同的文件中,这使得一切都更易于维护和更容易找到。

一篇文章@kelyvinn从2016年开始,目的是演示模块化,包括以下代码:

// controllers/apis/dogs/index.js
const
express = require('express'),
dogService = require('../../../services/dogs');


let router = express.Router();


router.get('/', dogService.getDogs);
router.get('/:id', dogService.getDogWithId);


module.exports = router;