繁体   English   中英

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

[英]Node.js / Express.js - How to override/intercept res.render function?

我正在使用Connect / Express.js构建一个Node.js应用,我想在将其转发到原始渲染功能之前拦截res.render(view,option)函数来运行一些代码。

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

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

我对JavaScript的OOP和原型继承的了解有点薄弱。 我该怎么做?


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

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

似乎有效。 当我为每个请求创建一个新的响应包装对象时,不确定是否是最好的解决方案,这会成为问题吗?

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

您可以使用/编写中间件。 起初,这个概念对我来说有点令人生畏,但经过阅读后,它变得更有意义了。 只是对于某些阅读本文的人来说,重写res.render的动机是提供全局视图变量。 我希望session在所有模板中都可用,而无需在每个res对象中都键入session

基本的中间件格式是。

app.use( function( req, res, next ) {
    //....
    next();
} );

下一个参数和函数调用对于执行至关重要。 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();
} );

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

app.use( '/myspcificurl', function( req, res, next ) {...} );

使用中间件覆盖它们的每个实例的响应或请求方法不是一个好主意,因为对于每个请求都执行中间件,并且每次调用该中间件时,您都使用cpu以及内存,因为您正在创建一个新功能。

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

req => express.request
res => express.response

因此,当您想为每个响应实例覆盖一个方法时,最好在其原型中覆盖它,因为一旦在每个响应实例中都可用,就可以覆盖它:

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

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

但是,最好入侵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);
};

最近,我发现自己需要做同样的事情,向每个模板提供特定于配置的Google Analytics(分析)媒体资源ID和Cookie域。

这里有很多很棒的解决方案。

我选择了与Lex提出的解决方案非常接近的解决方案,但是遇到了对res.render()的调用尚未包含现有选项的问题。 例如,以下代码在对extend()的调用中导致异常,因为未定义选项:

return res.render('admin/refreshes');

我添加了以下内容,说明了可能的各种参数组合,包括回调。 类似的方法可以与其他人提出的解决方案一起使用。

app.use(function(req, res, next) {
  var _render = res.render;
  res.render = function(view, options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = {};
    } else if (!options) {
      options = {};
    }
    extend(options, {
      gaPropertyID: config.googleAnalytics.propertyID,
      gaCookieDomain: config.googleAnalytics.cookieDomain
    });
    _render.call(this, view, options, callback);
  }
  next();
});

编辑:事实证明,当我需要实际运行一些代码时,虽然这一切可能都很方便,但是有一种非常简单的方法可以完成我尝试做的事情。 我再次查看了Express的源代码和文档,结果发现app.locals用于呈现每个模板。 因此,就我而言,我最终将以下所有工作替换为所有中间件代码:

app.locals.gaPropertyID = config.googleAnalytics.propertyID;
app.locals.gaCookieDomain = config.googleAnalytics.cookieDomain;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM