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


298

在询问之前,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.routerexpress.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()是我的信息的缓存,如果应用程序找不到请求的页面,它将检查缓存以查看其是否存在。


18
感谢您提出这个问题。我一直在谷歌搜索以找到此答案(以及提示问题的答案)。
哈里·塞尔登

8
那是一个写得很好的问题,我在搜索同一件事。
肯(Kirn)2013年

Answers:


329

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


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

例如,express.static('/var/www')将提供该文件夹中的文件。所以到您的节点服务器的请求,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”将发送到浏览器。

通常,您希望将路由器放置在静态中间件上方,以免意外命名的文件无法覆盖您的路由之一。

请注意,如果你不明确userouter,它是隐含快递在您定义的路线点添加(这就是为什么你的路线仍然工作,即使你注释掉app.use(app.router))。


评论者提出了关于顺序的另一点,staticrouter我尚未解决:对应用程序整体性能的影响。

use router上面的另一个原因static是优化性能。如果您放static第一把,那么您将在每个单独的请求上访问硬盘驱动器,以查看文件是否存在。在快速测试中,我发现在卸载的服务器上,此开销总计约为1ms。(该数目很可能在负载下会更高,在那里请求将争夺磁盘访问权限。)

有了router第一次,从来没有匹配的路由请求有打盘,节省了宝贵的毫秒。

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

最好的选择是将所有静态资源放在特定的文件夹下。(IE /static)然后可以安装static到该路径,以便仅在路径开头为时运行/static

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

在这种情况下,您可以将其放在上方router。如果存在文件,这可以避免处理其他中间件/路由器,但是老实说,我怀疑您会获得这么多。

您还可以使用staticCache,它在内存中缓存静态资源,这样您就不必在磁盘上存放常用文件。(警告: staticCache 显然将来会被删除。)

但是,我认为不staticCache缓存否定答案(当文件不存在时),因此如果您未将其挂载到路径staticCacherouter而放在上面,这将无济于事。

与所有有关性能的问题一样,请对实际应用(在负载下)进行测量并进行基准测试,以查看瓶颈所在。


快递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中更改的更多信息。


2
router在一个地方去。如果你第一次打电话app.get(或post或其他),你还没有useð app.router,快速添加它。
josh3736 2012年

4
@MikeCauser:否,因为磁盘访问(以查看文件是否存在)的开销大于函数调用的开销。在我的测试中,卸载服务器上的开销总计为1ms。在负载下,请求将竞争磁盘访问,这很有可能会更高。使用staticafter router,关于其他中间件的问题变得无关紧要,因为它必须在路由器之上。
josh3736

2
精彩的解释!非常感谢!
肯(Kirn)2013年

3
app.router在当前的master分支(即express-4.0)中删除。每个路由都成为一个单独的中间件。
yanychar 2014年

3
我对此进行了进一步的澄清。在Express 4中,可以将多个路由分配给路由器,然后使用该路由器,为该路由器指定根路径,并通过app.use(path,router)将其放置在“中间件”堆栈中。这样一来,相关路由就可以使用各自的路由器,并以一个基本路径为单位进行分配。如果我对它的理解更好,我将提出另一个答案。再次,我从scotch.io/tutorials/javascript/…获得
Joe Lapp

2

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

在Express 4.0 Router中,在定义路由时,我们比以往拥有更大的灵活性。

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

0

在快速版本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

为什么采用这种方法:

采用这种方法的主要优点是现在我们的应用程序更加模块化。现在,可以将特定路由的所有路由处理程序放入不同的文件中,这使得所有内容都更易于维护和查找。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.