中间件和app.use在Expressjs中实际上意味着什么?


228

我看到的几乎每个Express应用程序都有app.use关于中间件的声明,但是我没有找到关于中间件实际上是什么以及该app.use声明在做什么的清晰,简洁的解释。甚至快递文档本身对此也含糊不清。您能为我解释这些概念吗?


3
参考类似的问题(前面寿这个被创造):stackoverflow.com/questions/11321635/...
ericsoco

43
^哈!这两个问题在注释中相互引用。
朱利安·林

17
所以这是一个循环参考。
史蒂夫·K

6
Express.js Middleware揭秘关于该主题的精彩博客文章。这是以前复制粘贴在这里的,当然是窃,但是原始帖子仍然很有帮助,因此我在这里留下了链接。
totymedli

1
我写了一篇关于express.js中间件的文章。这是链接:nodexplained.com/blog-detail/2017/12/31/…–
shrawan_lakhe

Answers:


111

中间件

我在一个新项目中将中间件的概念分离了一半。

中间件使您可以定义应执行的操作堆栈。Express服务器本身就是一堆中间件。

// express
var app = express();
// middleware
var stack = middleware();

然后您可以通过调用将层添加到中间件堆栈 .use

// express
app.use(express.static(..));
// middleware
stack.use(function(data, next) {
  next();
});

中间件堆栈中的一层是一个函数,该函数接受n个参数(express req和&表示2个res)和一个next函数。

中间件希望该层进行一些计算,增加参数,然后调用next

除非您处理堆栈,否则堆栈不会执行任何操作。每当服务器上捕获到传入的HTTP请求时,Express都会处理堆栈。使用中间件,您可以手动处理堆栈。

// express, you need to do nothing
// middleware
stack.handle(someData);

一个更完整的例子:

var middleware = require("../src/middleware.js");

var stack = middleware(function(data, next) {
    data.foo = data.data*2;
    next();
}, function(data, next) {
    setTimeout(function() {
        data.async = true;
        next();
    }, 100)
}, function(data) {
    console.log(data);
});

stack.handle({
    "data": 42
})

用快速术语来说,您只需定义要快速处理的每个传入HTTP请求的操作堆栈。

就快速(而非连接)而言,您具有全局中间件和特定于路由的中间件。这意味着您可以将中间件堆栈附加到每个传入的HTTP请求,或仅将其附加到与特定路由交互的HTTP请求。

快递和中间件的高级示例:

// middleware 

var stack = middleware(function(req, res, next) {
    users.getAll(function(err, users) {
        if (err) next(err);
        req.users = users;
        next();  
    });
}, function(req, res, next) {
    posts.getAll(function(err, posts) {
        if (err) next(err);
        req.posts = posts;
        next();
    })
}, function(req, res, next) {
    req.posts.forEach(function(post) {
        post.user = req.users[post.userId];
    });

    res.render("blog/posts", {
        "posts": req.posts
    });
});

var app = express.createServer();

app.get("/posts", function(req, res) {
   stack.handle(req, res); 
});

// express

var app = express.createServer();

app.get("/posts", [
    function(req, res, next) {
        users.getAll(function(err, users) {
            if (err) next(err);
            req.users = users;
            next();  
        });
    }, function(req, res, next) {
        posts.getAll(function(err, posts) {
            if (err) next(err);
            req.posts = posts;
            next();
        })
    }, function(req, res, next) {
        req.posts.forEach(function(post) {
            post.user = req.users[post.userId];
        });

        res.render("blog/posts", {
            "posts": req.posts
        });
    }
], function(req, res) {
   stack.handle(req, res); 
});

4
嗯...在这种情况下,中间件是您自己的库还是Express的一部分?
iZ。

5
凉。我仍然对app.use()语法有些困惑。中间件的实际返回值是use多少?
iZ。

9
@iZ use将其添加到堆栈中。然后,每个单个请求都会通过堆栈。
雷诺斯2011年

7
@Raynos,指向您的项目“中间件”的链接,已断开。

1
@Raynos但我看到Express中仍在使用中间件吗?这是什么意思?
Timo Huovinen

60

简化之后,Web服务器可以看作是接受请求并输出响应的功能。因此,如果您将Web服务器视为一种功能,则可以将其组织成几部分并将其分成较小的功能,这样它们的组成将成为原始功能。

中间件是可以与其他组件组合的较小函数,显而易见的好处是可以复用它们。


33

我添加一个较晚的答案,以添加先前答案中未提及的内容。

到现在为止,应该清楚中间件是在客户端请求服务器answer之间运行的功能。所需的最常见中间件功能是错误管理,数据库交互,从静态文件或其他资源获取信息。要在中间件堆栈上移动,必须调用下一个回调,您可以在中间件函数的末尾看到它,以移至流程的下一步。

您可以使用app.use方法,并有一个流程是这样

var express = require('express'),
    app = express.createServer(),                                                                                                                                                 
    port = 1337;

function middleHandler(req, res, next) {
    console.log("execute middle ware");
    next();
}

app.use(function (req, res, next) {
    console.log("first middle ware");                                                                                                             
    next();
});

app.use(function (req, res, next) {
    console.log("second middle ware");                                                                                                             
    next();
});

app.get('/', middleHandler, function (req, res) {
    console.log("end middleware function");
    res.send("page render finished");
});

app.listen(port);
console.log('start server');

但是您也可以使用另一种方法,并将每个中间件作为函数参数传递。这是来自MooTools Node.js网站示例,在示例中,中间件软件将Twitter,Github和Blog流response发送回给客户端之前。注意函数如何在中作为参数传递app.get('/', githubEvents, twitter, getLatestBlog, function(req, res){。使用app.get将仅针对GET请求app.use被调用,而将针对所有请求被调用。

// github, twitter & blog feeds
var githubEvents = require('./middleware/githubEvents')({
    org: 'mootools'
});
var twitter = require('./middleware/twitter')();
var blogData = require('./blog/data');
function getLatestBlog(req, res, next){
    blogData.get(function(err, blog) {
        if (err) next(err);
        res.locals.lastBlogPost = blog.posts[0];
        next();
    });
}

// home
app.get('/', githubEvents, twitter, getLatestBlog, function(req, res){
    res.render('index', {
        title: 'MooTools',
        site: 'mootools',
        lastBlogPost: res.locals.lastBlogPost,
        tweetFeed: res.locals.twitter
    });
});

2
我一直在寻找Express.js是否支持基于路由(基于非路由器)中间件安装的答案看来您已经在答案中显示了它。
塞琉克

您能解释一下上面的示例吗?如何将这么多函数传递到app.get(...)中,它们的调用顺序是什么?
Tanner Summers

2
@TannerSummers,您好,该.get()方法使用3种类型的参数:第一个,最后一个和中间一个。在内部,它检测是否有超过2个参数,并将这些参数(中间的参数)用作中间件函数,并从左至右调用它们。
塞尔焦

22

expressjs 指南对您的问题的回答很简洁,我强烈建议您阅读该指南,我正在发布该指南的一小段内容,该指南相当不错。

编写用于Express应用程序的中间件

总览

中间件功能是可以访问请求对象 req),响应对象 res)和应用程序的请求-响应周期中的下一个功能的函数。下一个功能是Express路由器中的功能,该功能在被调用时将在当前中间件之后执行中间件。

中间件功能可以执行以下任务:

  • 执行任何代码。
  • 更改请求和响应对象。
  • 结束请求-响应周期。
  • 调用堆栈中的下一个中间件。

如果当前中间件函数没有结束请求-响应周期,则它必须调用next()将控制权传递给下一个中间件函数。否则,该请求将被挂起。

在此处输入图片说明

这是一个简单的“ Hello World” Express应用程序的示例。本文的其余部分将定义并向应用程序添加两个中间件功能:一个名为myLogger的用户,它输出一条简单的日志消息,另一个名为requestTime 1,其显示HTTP请求的时间戳。

var express = require('express')
var app = express()

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000)   

中间件功能myLogger

这是一个称为“ myLogger”的中间件功能的简单示例。当对应用程序的请求通过时,此功能仅显示“已记录”。中间件功能已分配给名为myLogger的变量。

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

注意上面对next()的调用。调用此函数将调用应用程序中的下一个中间件函数。在接下来的()函数不是Node.js的或快递API的一部分,但传递给中间件功能的第三个参数。在接下来的()函数可以被命名为任何东西,但是按照惯例,它总是被命名为“下一个”。为避免混淆,请始终使用此约定。

要加载中间件函数,请调用app.use(),指定中间件函数。例如,以下代码在路由到根路径(/)之前加载myLogger中间件函数。

var express = require('express')
var app = express()

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000)

每次应用程序收到请求时,它将消息“ LOGGED”打印到终端。

中间件加载的顺序很重要:首先加载的中间件功能也将首先执行。

如果myLogger是在到达根路径的路由之后加载的,则该请求永远不会到达该请求,并且该应用不会打印“ LOGGED”,因为根路径的路由处理程序会终止请求-响应周期。

中间件函数myLogger仅打印一条消息,然后通过调用next()函数将请求传递到堆栈中的下一个中间件函数。


  1. 这篇文章仅包含myLogger中间件,有关进一步的文章,您可以转到此处的原始expressjs指南。


1
很好的解释。
Drumbeg

它可以在Express网站上的expressjs.com/en/guide/writing-middleware.html上获得,这真的很好。我不知道为什么到目前为止没有人提到它。
Suraj Jain

2
好一个。这是我在这里看到的最清晰的解释,是的,很奇怪,没有人引用它!
Drumbeg

1
很好的解释
Rehan Shikkalgar '18

中间件加载的顺序很重要:首先加载的中间件功能也将首先执行。:这是一个重要的注意事项。没有其他答案提到这一点。对于只使用python的初学者来说,这是非常重要的,因为这些事情可能从未遇到过。
Tessaracter

11

=====非常简单的解释=====

中间件通常在Express.js框架的上下文中使用,并且是node.js的基本概念。简而言之,它基本上是一种可以访问应用程序的请求和响应对象的功能。我想考虑的方式是,在应用程序处理请求之前,要经历一系列“检查/预屏”。例如,中间件非常适合在请求进入应用程序之前确定请求是否已通过身份验证,如果请求未通过身份验证或用于记录每个请求,则返回登录页面。有许多第三方中间件可供使用,这些中间件可以启用各种功能。

简单的中间件示例:

var app = express();
app.use(function(req,res,next)){
    console.log("Request URL - "req.url);
    next();
}

上面的代码将针对每个进入的请求执行,并记录请求的URL,next()方法实质上允许程序继续。如果未调用next()函数,则该程序将不会继续进行,并且会在中间件执行时停止。

几个中间件陷阱:

  1. 中间件在您的应用程序中的顺序很重要,因为请求将按顺序进行。
  2. 忘记在中间件函数中调用next()方法可能会中断请求的处理。
  3. 中间件功能中对req和res对象的任何更改都会使更改对使用req和res的应用程序的其他部分可用

1
非常感谢你!这是迄今为止了解这一点的最好解释。一个问题,我正在用中间件阅读一些代码,next()但它不会调用而是return next()。有什么区别?
KansaiRobot

感谢很多朋友的客气话...我们这样做next()是因为我们希望下一个中间件被调用,我不认为next()or return next()应该有所不同!仍然取决于代码是什么...
Vaibhav Bacchav

7

中间件是在输入/源之后在中间执行的函数,然后产生一个输出,该输出可以是最终输出,也可以由下一个中间件使用,直到循环完成为止。

就像产品流经装配线一样,在生产过程中它会不断进行修改,直到完成,评估或被拒绝为止。

中间件期望某些值(即参数值)起作用,并且基于某种逻辑,中间件将调用或不调用下一个中间件或将响应发送回客户端。

如果您仍然不能掌握中间件的概念,则它的方式类似于Decorator或Command-chain模式。


5

中间件是Express js路由层在调用用户定义的处理程序之前调用的链接函数的子集。中间件功能具有对请求和响应对象的完全访问权限,并且可以对其进行修改。

中间件链总是按定义时的确切顺序调用,因此对您确切地了解特定中间件的工作至关重要。
中间件函数完成后,它将通过调用其下一个参数作为函数来调用链中的下一个函数。
执行完完整的链后,将调用用户请求处理程序。


1

保持简单,伙计!

注意:答案与ExpressJS内置的中间件案例有关,但是中间件的定义和使用案例不同。

从我的角度来看,中间件充当实用程序或辅助函数,但通过使用,中间件的激活和使用是完全可选的,app.use('path', /* define or use builtin middleware */)因为我们不希望我们编写一些代码来执行客户端的每个HTTP请求所需的非常常见的任务例如处理Cookie,CSRF令牌和...,这在大多数应用程序中都很常见,因此中间件可以帮助我们以某种操作的堆栈,顺序或顺序为客户端的每个HTTP请求完成所有这些操作,然后将处理结果提供为客户请求的一个单位

例:

Web服务器技术的本质是接受客户请求并根据他们的请求向他们提供回复。

想象一下,如果我们仅以“你好,世界!”来回应。向我们的Web服务器的根URI发送GET HTTP请求的文本非常简单,不需要任何其他操作,但是如果我们正在检查当前登录的用户,然后以“你好,用户名!”作为响应,则不需要其他任何内容。在这种情况下,我们需要比平时更多的东西,我们需要一个中间件来处理所有客户端请求元数据,并向我们提供从客户端请求中获取的标识信息,然后根据该信息,我们可以唯一地标识当前用户,并且可以对他进行响应/她并提供一些相关数据。

希望它能帮助到某人!


-1

用最基本的术语来说,如果我想这样解释,我可以从traversymedia youtube频道速成课程中学习。
好的,所以中间件是一个函数,它在您这样调用路由后执行。

var logger = function(req, res, next){
   console.log('logging...');
   next();
}

app.use(logger);

每次刷新页面时都会执行此记录器功能,这意味着您可以在页面呈现任何操作api调用后基本编写任何内容,然后您可以在其中编写任何内容。并将此中间件放在中间件的路由功能顺序之前非常重要或不起作用

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.