Node.js/Express.js - 如何覆盖/拦截res.render函数?

Sun*_*oot 23 javascript connect prototypal-inheritance node.js express

我正在用Connect/Express.js构建一个Node.js应用程序,我想拦截res.render(视图,选项)函数来运行一些代码,然后再转发到原始的渲染函数.

app.get('/someUrl', function(req, res) {

    res.render = function(view, options, callback) {
        view = 'testViews/' + view;
        res.prototype.render(view, options, callback);
    };

    res.render('index', { title: 'Hello world' });
});
Run Code Online (Sandbox Code Playgroud)

它看起来像一个人为的例子,但它确实适合我正在构建的整体框架.

我对JavaScript的OOP和Prototypal继承的了解有点弱.我该怎么办?


更新:经过一些实验,我想出了以下内容:

app.get('/someUrl', function(req, res) {

    var response = {};

    response.prototype = res;

    response.render = function(view, opts, fn, parent, sub){
        view = 'testViews/' + view;
        this.prototype.render(view, opts, fn, parent, sub);
    };

    response.render('index', { title: 'Hello world' });
});
Run Code Online (Sandbox Code Playgroud)

它似乎工作.不确定它是否是最好的解决方案,因为我正在为每个请求创建一个新的响应包装器对象,这是一个问题吗?

Lex*_*Lex 27

老问题,但发现自己问同样的事情.如何拦截res渲染?现在使用express 4.0x.

您可以使用/编写中间件.这个概念起初对我来说有点令人生畏,但经过一些阅读后它更有意义.而对于其他读这篇文章的人来说,覆盖res.render的动机是提供全局视图变量.我希望session在我的所有模板中都可用,而不必在每个res对象中输入它.

基本的中间件格式是.

app.use( function( req, res, next ) {
    //....
    next();
} );
Run Code Online (Sandbox Code Playgroud)

下一个参数和函数调用对执行至关重要.next是回调函数,允许多个中间件在不阻塞的情况下完成它们的工作.有关更好的解释请阅读此处

然后可以使用它来覆盖渲染逻辑

app.use( function( req, res, next ) {
    // grab reference of render
    var _render = res.render;
    // override logic
    res.render = function( view, options, fn ) {
        // do some custom logic
        _.extend( options, {session: true} );
        // continue with original render
        _render.call( this, view, options, fn );
    }
    next();
} );
Run Code Online (Sandbox Code Playgroud)

我已经使用express 3.0.6测试了这段代码.它应该与4.x一起使用而没有问题.您还可以覆盖特定的URL组合

app.use( '/myspcificurl', function( req, res, next ) {...} );
Run Code Online (Sandbox Code Playgroud)


Ali*_*Ali 12

使用中间件来覆盖每个实例的响应或请求方法并不是一个好主意,因为中间件是针对每个请求执行的,每次调用它都会使用cpu和内存,因为你正在创建一个新功能.

您可能知道javascript是一种基于Prototype的语言,每个对象都有一个原型,如响应和请求对象.通过查看代码(表达4.13.4),您可以找到他们的原型:

req => express.request
res => express.response
Run Code Online (Sandbox Code Playgroud)

因此,当您想要为响应的每个单个实例覆盖一个方法时,在它的原型中覆盖它会好得多,因为它在每个响应实例中都可用:

var app = (global.express = require('express'))();
var render = express.response.render;
express.response.render = function(view, options, callback) {
    // desired code
    /** here this refer to the current res instance and you can even access req for this res: **/
    console.log(this.req);
    render.apply(this, arguments);
};
Run Code Online (Sandbox Code Playgroud)

  • 我不知道为什么这个评论不被接受!这是绝对正确的答案! (2认同)

Lin*_*iel 7

response对象没有原型.这应该有效(将Ryan的想法放在中间件中):

var wrapRender = function(req, res, next) {
  var _render = res.render;
  res.render = function(view, options, callback) {
    _render.call(res, "testViews/" + view, options, callback);
  };
};
Run Code Online (Sandbox Code Playgroud)

但是,破解ServerResponse.prototype可能更好:

var express = require("express")
  , http = require("http")
  , response = http.ServerResponse.prototype
  , _render = response.render;

response.render = function(view, options, callback) {
  _render.call(this, "testViews/" + view, options, callback);
};
Run Code Online (Sandbox Code Playgroud)