我看到的几乎每个Express应用程序都有一个app.use语句用于中间件,但我还没有找到一个清晰、简洁的解释来说明中间件到底是什么以及app.use语句在做什么。甚至express文档本身在这一点上也有点模糊。你能给我解释一下这些概念吗?


当前回答

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

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

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

如果您仍然不能理解中间件的概念,那么它在某种程度上类似于Decorator或命令链模式。

其他回答

中间件

在一个新项目中,我已经把中间件的概念分离了一半。

中间件允许您定义应该流经的操作堆栈。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的高级示例:

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

expressjs的指南非常简洁地回答了你的问题,我强烈建议你去阅读,我发布了一个简短的指南片段,指南是相当好的。

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

概述

中间件函数是能够访问请求对象(req)、响应对象(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指南这里。

经过简化之后,web服务器可以被看作是一个接收请求并输出响应的函数。因此,如果您将web服务器视为一个功能,您可以将其组织成几个部分,并将它们分离为更小的功能,以便它们的组合将是原始功能。

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

我添加了一个后期的答案,添加一些在之前的答案中没有提到的东西。

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

你可以使用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 Nodejs网站的例子,中间件在响应被发送回客户端之前获得Twitter、Github和Blog的流。注意这些函数是如何在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
    });
});

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

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

app.use(logger);

这个记录器函数在你每次刷新页面时执行这意味着你可以在页面呈现后写任何你需要做的事情任何api调用,重置基本上任何事情。把这个中间件放在你的路由功能之前,中间件的顺序非常重要,否则它就无法工作