Подтвердить что ты не робот

Node.js/Express.js - Как переопределить/перехватить функцию res.render?

Я создаю приложение Node.js с Connect/Express.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 немного слабы. Как мне сделать что-то подобное?


Обновление: После некоторых экспериментов я придумал следующее:

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

Кажется, что сработало. Не уверен, что это лучшее решение, поскольку я создаю новый объект оболочки ответа для каждого запроса, будет ли это проблемой?

4b9b3361

Ответ 1

Старый вопрос, но обнаружил, что спрашиваю то же самое. Как перехватить res render? Теперь с помощью express 4.0x.

Вы можете использовать/написать промежуточное программное обеспечение. Поначалу концепция была немного сложной, но после некоторого чтения это немного пошло. И только для некоторого контекста для тех, кто читает это, мотивация переопределения res.render заключалась в предоставлении глобальных переменных представления. Я хочу, чтобы session был доступен во всех моих шаблонах без необходимости вводить его в каждый res-объект.

Основной формат промежуточного программного обеспечения.

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

Ответ 2

Объект response не имеет прототипа. Это должно сработать (принимая идею Райана поместить ее в промежуточное ПО):

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

Ответ 3

Не рекомендуется использовать промежуточное программное обеспечение для переопределения метода ответа или запроса для каждого из них, потому что промежуточное программное обеспечение выполняется для каждого запроса, и каждый раз, когда он получает вызов, вы используете процессор, а также память, потому что вы создают новую функцию.

Как вы знаете, javascript - это язык, основанный на Prototype, и каждый объект имеет прототип, например, объекты ответа и запроса. Просмотрев код (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);
};

Ответ 4

Недавно мне было нужно сделать то же самое, чтобы предоставить идентификатор свойства Google Analytics и домен cookie для каждого из моих шаблонов.

Здесь есть множество отличных решений.

Я решил пойти с чем-то очень близким к решению, предложенным Лексом, но столкнулся с проблемами, когда вызовы res.render() уже не включали существующие параметры. Например, следующий код вызывал исключение в вызове extend(), поскольку параметры были undefined:

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

edit:. Оказывается, что, хотя все это может быть удобно, когда мне нужно фактически запустить какой-то код, есть чрезвычайно простой способ выполнить то, что я пытался сделать. Я снова посмотрел на источник и документы для Express, и выясняется, что app.locals используются для визуализации каждого шаблона. Поэтому в моем случае я в конечном итоге заменил весь код промежуточного программного обеспечения выше со следующими назначениями:

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