中间件和 app.use 在 Expressjs 中的实际含义是啥?

Posted

技术标签:

【中文标题】中间件和 app.use 在 Expressjs 中的实际含义是啥?【英文标题】:What does middleware and app.use actually mean in Expressjs?中间件和 app.use 在 Expressjs 中的实际含义是什么? 【发布时间】:2011-11-12 08:20:25 【问题描述】:

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

【问题讨论】:

类似问题供参考(虽然这个问题是较早创建的):***.com/questions/11321635/… ^ 哈!这两个问题在 cmets 中相互引用。 所以它是一个循环参考。 我写了一篇关于express.js中间件的文章。这是链接:nodexplained.com/blog-detail/2017/12/31/… 【参考方案1】:

middleware

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

中间件允许您定义一系列您应该通过的操作。 Express 服务器本身就是一堆中间件。

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

然后你可以通过调用.use向中间件堆栈添加层

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

中间件堆栈中的一层是一个函数,它接受 n 个参数(2 个用于 express,req & 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 请求定义要快速处理的操作堆栈。

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

express 和中间件的高级示例:

// 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); 
);

【讨论】:

嗯...在这种情况下是中间件你自己的库还是 express 的一部分? 酷。我仍然对app.use() 语法感到有些困惑。中间件的实际返回值是多少,use 用它做什么? @iZ 使用将其添加到堆栈中。然后每个请求都会通过堆栈。 @Raynos,您的项目“中间件”的链接已损坏。 @Raynos 但我看到 Express 中仍在使用中间件?你是什​​么意思它被核弹了?【参考方案2】:

在简化之后,Web 服务器可以看作是一个接收请求并输出响应的函数。因此,如果您将 Web 服务器视为一个函数,您可以将它组织成多个部分,然后将它们分成更小的函数,这样它们的组合就会成为原始函数。

中间件是您可以与其他人组合的较小的功能,其明显的好处是您可以重复使用它们。

【讨论】:

【参考方案3】:

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

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

您可以使用app.use 方法并拥有一个流程like this:

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

但您也可以使用另一种方法并将每个中间件作为函数参数传递。这是一个example from the MooTools Nodejs website,中间件在将response 发送回客户端之前获取Twitter、Github 和博客流。注意函数是如何在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
    );
);

【讨论】:

我正在寻找Express.js 是否支持基于路由(而不是基于路由器)的中间件安装的答案?看来你已经在你的答案中展示了它。跨度> 你能解释一下你上面的例子吗?你怎么能把这么多的函数传入 app.get(...) 以及它们的调用顺序是什么? 嗨@TannerSummers,.get() 方法接受3 种类型的参数:第一个、最后一个和中间的。在内部,它检测是否有超过 2 个的参数,并将这些(中间的)用作中间件函数,从左到右调用它们。 我不确定这两种风格是否有区别。这篇文章有助于澄清 - 谢谢!【参考方案4】:

expressjs guide 对您的问题有很好的回答,我强烈建议您阅读,我正在发布该指南的简短 sn-p,该指南非常好。

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

概述

中间件函数是可以访问request object (req)、response object (res),以及应用程序请求-响应周期中的下一个函数。下一个函数是 Express 路由器中的一个函数,当被调用时,它会在当前中间件之后执行中间件。

中间件函数可以执行以下任务:

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

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

示例

这是一个简单的“Hello World”Express 应用程序示例。本文的其余部分将定义并向应用程序添加两个中间件函数:一个称为 myLogger 打印简单的日志消息,另一个称为 requestTime1 显示 HTTP 请求的时间戳

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

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

app.listen(3000)   

中间件函数myLogger

这是一个名为“myLogger”的中间件函数的简单示例。当对应用程序的请求通过它时,此函数只会打印“LOGGED”。中间件函数分配给名为 myLogger 的变量。

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

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

要加载中间件函数,调用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() 函数将请求传递给堆栈中的下一个中间件函数。


    这篇文章将只包含myLogger中间件,更多的帖子你可以去原始的expressjs指南here。

【讨论】:

很好的解释。 可以在expressjs.com/en/guide/writing-middleware.html的快递网站上找到,真的很好。我想知道为什么到目前为止没有人提到它。 不错的一个。这是我在这里看到的最清晰的解释,是的,奇怪的是没有人引用它! 很好解释 中间件加载的顺序很重要:首先加载的中间件函数也会先执行。:这是一个非常重要的注意事项。没有其他答案提到这一点。对于只使用过 python 的初学者来说,这非常重要,因为这些事情可能从未遇到过。【参考方案5】:

=====很简单的解释=====

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

简单的中间件示例:

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

上面的代码会为每个进来的请求执行,并记录请求的 url,next() 方法本质上允许程序继续。如果没有调用 next() 函数,程序将不会继续执行,并会在中间件执行时停止。

几个中间件陷阱:

    应用程序中中间件的顺序很重要,因为请求会按顺序通过每个中间件。 忘记调用中间件函数中的 next() 方法可能会停止处理您的请求。 任何更改中间件函数中的 req 和 res 对象,都会使更改可用于使用 req 和 res 的应用程序的其他部分

【讨论】:

非常感谢!这是迄今为止理解这一点的最佳解释。一个问题,我正在阅读一些带有中间件的代码,它不调用next(),而是调用return next()。有什么区别? 非常感谢朋友的好话...我们这样做next() 因为我们希望调用下一个中间件,我不认为next()return next() 应该有任何区别!仍然取决于代码是什么......【参考方案6】:

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

它就像一个经过装配线的产品,它在移动过程中不断修改,直到完成、评估或被拒绝。

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

如果你仍然不能掌握中间件的概念,它的方式类似于装饰器或命令链模式。

【讨论】:

【参考方案7】:

中间件是 Express js 路由层在调用用户定义的处理程序之前调用的链接函数的子集。中间件函数可以完全访问请求和响应对象,并且可以修改其中任何一个。 中间件链总是按照其定义的确切顺序被调用,因此准确了解特定中间件在做什么对您来说至关重要。 中间件函数完成后,它会调用中的下一个函数链通过调用它的下一个参数作为函数。 完整链执行完毕后,调用用户请求处理程序。

【讨论】:

【参考方案8】:

保持简单,伙计!

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

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

例子:

接受客户的请求并根据他们的请求向他们提供响应是网络服务器技术的本质。

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

希望对某人有所帮助!

【讨论】:

【参考方案9】:

在非常基本的术语中,如果我想这样解释它,我会从 traversymedia youtube 频道快速速成课程中学到这一点。 好的,所以中间件是在你像这样调用你的路由后执行的函数。

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


app.use(logger);

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

【讨论】:

以上是关于中间件和 app.use 在 Expressjs 中的实际含义是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Express JS 中的中间件是啥

带有ExpressJS的GET方法表单-多个app.get

如何使用基于网站的 ExpressJs 设置 CORS 和 JWT 令牌验证

typeerror: app.use() 需要中间件函数

app.use(cors()) 是做啥的?

在运行时添加路由 (ExpressJs)