【问题标题】:Understanding middleware in Express了解 Express 中的中间件
【发布时间】:2023-03-12 15:13:01
【问题描述】:

我试图弄清楚中间件在 Express 中是如何工作的。

虽然我了解中间件的概念,但我对中间件参数感到困惑。

这是来自the offical docs regarding middleware的示例:

app.use('/user/:id', function (req, res, next) {
   console.log('Request URL:', req.originalUrl)
   next()
    }, function (req, res, next) {
      console.log('Request Type:', req.method)
      next()
    })

在这个例子中,我可以看到有两个函数充当两个中间件,在处理这个特定路由之前一个接一个地执行。

但是传递给这些函数的参数是什么?

reqres 只是“空”对象吗?

如果是这样,我们如何能够引用属性req.originalUrl

如果不是,该对象及其属性从何而来?

他们还在教程中使用res.send,因此res 对象似乎也有属性,而不是“空”对象。

(我知道next 是一个回调参数)。

【问题讨论】:

    标签: node.js express middleware


    【解决方案1】:

    总结

    request object 代表 HTTP 请求,具有请求查询字符串、参数、正文、HTTP 标头等属性。

    response object 表示 Express 应用在收到 HTTP 请求时发送的 HTTP 响应。

    Middleware functions 是在应用程序的请求-响应周期中可以访问请求对象、响应对象和next 函数的函数。 next 函数是 Express 路由器中的一个函数,当被调用时,它会在当前中间件之后执行中间件。

    路由可以附加chained methods(用于GETPOSTDELETE 请求),将中间件函数作为参数。

    请求对象是最初从请求中接收到的数据,可以通过各种中间件函数进行修改,响应对象是发送出去的数据。

    示例中间件

    下面是一个示例中间件函数,您可以在应用开头复制和粘贴:

    /**
     * An example of a middleware function that logs various values of the Express() request object.  
     * 
     * @constant  
     * @function  
     * @param  {object} req - The req object represents the HTTP request and has properties for the request query string, parameters, body, HTTP headers, and so on. In this documentation and by convention, the object is always referred to as req (and the HTTP response is res) but its actual name is determined by the parameters to the callback function in which you’re working.
     * @param  {object} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.  In this documentation and by convention, the object is always referred to as res (and the HTTP request is req) but its actual name is determined by the parameters to the callback function in which you’re working.
     * @param  {Function} next - `next` is used as an argument in the middleware function, and subsequently invoked in the function with `next()`, to indicate the application should "move on" to the next piece of middleware defined in a route's chained method.  
     * @see {@link https://expressjs.com/en/4x/api.html#req|Express Request}  
     * @see {@link https://expressjs.com/en/4x/api.html#res|Express Response}  
     * @see {@link http://expressjs.com/en/guide/writing-middleware.html|Writing Middleware}  
     */
    const my_logger = (req, res, next) => {
        console.log("req.headers: ");
        console.log(req.headers);
        console.log("req.originalUrl: " + req.originalUrl);
        console.log("req.path: " + req.path);
        console.log("req.hostname: " + req.hostname);
        console.log("req.query: " + JSON.stringify(req.query));
        console.log("req.route: " + JSON.stringify(req.route));
        console.log("req.secure: " + JSON.stringify(req.secure));
        console.log("req.ip: " + req.ip);
        console.log("req.method: " + req.method);
        console.log("req.params:");
        console.log(req.params);
        console.log("==========================");
    
        //if next() is not invoked below, app.use(myLogger) is the only middleware that will run and the app will hang
        next();
    
    }
    
    // called for all requests
    app.use(my_logger);
    

    示例路线

    以下是一些示例路线。

    路由附加了chained methods,将中间件函数作为参数。

    // some example routes
    app.route("/api/:api_version/pages")
        .get(api_pages_get);
    
    app.route("/api/:api_version/topics")
        .get(api_topics_get)
        .post(api_login_required, api_topics_post) 
        .delete(api_login_required, api_topics_delete);   
    
    app.route("/api/:api_version/topics/ratings")
        .post(api_login_required, api_topics_ratings_post);
    

    在中间件函数中使用next()

    在上面的例子中,你可以看到一些方法有两个中间件函数作为参数。

    第一个 api_login_required 验证登录凭据,如果成功,则调用 next() 提示运行下一个中间件函数。

    看起来像这样:

    const api_login_required = (req, res, next) => {
    
        // req.user exists if the user's request was previously verified, it is produced elsewhere in the code   
        if (req.user) {
            next();
        } else {
            return res.status(401).json({ message: 'Unauthorized user!' });
        }
        
    }
    

    没有next()的中间件

    但是,附加到 /api/:api_version/pages 的路由处理程序的 get() 方法只有一个中间件函数参数:api_pages_get

    如下图,api_pages_get不调用next(),因为它之后不需要运行中间件函数。

    它使用响应对象的send()json() 方法返回响应。

    const api_pages_get = async (req, res) => {
    
        var page_title = req.query.page_title;
    
        var collection = mongo_client.db("pages").collection("pages");
        var query = { page_title: page_title };
        var options = { projection: { page_title: 1, page_html: 1 } };
    
        try {
            var page = await collection.findOne(query);
    
            // if there is no result
            if (page === null) {
                res.status(404).send('404:  that page does not exist');
                return;
            }
            // if there is a result
            else if (page !== null) {
                res.json(page);
                return;
            }
        } catch (err) {
            console.log("api_pages_get() error: " + err);
            res.send(err);
            return;
        }
    
    }
    

    中间件注意事项

    我之前写的一些其他笔记供我自己参考,可能会有所帮助:

    中间件或中间件函数可以访问 Express requestresponse 对象,并作为参数传递给路由的链式方法(如果作为参数传递给 use() 的实例,则在所有请求中传递)方法在代码的早期定义)。

    next 用作中间件函数中的参数,随后在函数中使用next() 调用,以指示应用程序应该“继续”到定义在路由的链式方法中的下一个中间件。

    如果一个中间件函数没有调用next(),它就不会移动到路由或方法处理程序中定义的下一个中间件。

    另外,如果没有使用next(),并且函数中没有定义终止动作,即响应,则应用将处于“挂起”状态。

    【讨论】:

      【解决方案2】:

      req 和 res 只是“空”对象吗?

      不,reqres 永远不会为空,实际上它们是相同的,它们被传递给每个中间件。您还可以修改reqres 对象,修改将持续存在于所有后续中间件中。

      您可以在此处分别查看 reqres 上的所有可用字段 - request objectresponse object

      您始终可以在中间件的任何位置访问reqres。如果您希望结束请求响应周期,您可以使用响应对象并发送类似res.send(200) 的响应。这将结束 req-res 循环,您无需调用 next()

      但是这个函数有什么参数呢?

      您不需要向此函数传递任何参数。 Express 总是将reqresnext 传递给任何定义的中间件。这是您可以假设 express 使用的格式,并且所有中间件都应该遵循。

      请注意,如果您不结束 req-res 循环,则必须调用 next(),它将控制权传递给下一个中间件。如果中间件没有结束 req-res 循环并且也没有调用 next(),请求将一直挂起,并且可能只是在客户端超时。

      【讨论】:

      • 谢谢。如果它们不是空的,那么在我对此对象进行任何修改之前,req 和 res 中存在什么?我可以访问什么以及我如何知道我可以访问什么?
      • @BenJ 我编辑了答案。您可以点击链接,其中包含 req 和 res 对象上的所有可用属性和方法
      【解决方案3】:

      如果我理解正确的话,令人困惑的部分是传递给中间件函数的对象吗?在您链接的文档中,已经对这些进行了解释(见下文)。

      • “但是这个函数的参数是什么?。”

      中间件函数是可以访问 request object (req)response object (res) 和应用程序请求-响应周期中的下一个中间件函数的函数。 next 中间件函数通常由一个名为 next 的变量表示。

      (Source)

      • “req 和 res 只是“空”对象吗?如果是,我们为什么要使用字段 req.orginaleUrl?如果不是,那么该对象在哪里? 它的字段来自于?”

      如果您点击链接,您会发现请求对象的以下说明:

      req 对象表示 HTTP 请求,具有请求查询字符串、参数、正文、HTTP 标头等属性。

      (Source)

      您问题中提到的originalUrl 属性是req 对象的属性。

      和响应对象:

      res 对象表示 Express 应用在收到 HTTP 请求时发送的 HTTP 响应。

      send 是分配给 res 对象的方法,它将发送 HTTP 响应。

      (Source)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-14
        • 1970-01-01
        • 1970-01-01
        • 2014-07-29
        • 1970-01-01
        • 1970-01-01
        • 2014-10-23
        相关资源
        最近更新 更多