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

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

23
задан Sunday Ironfoot 17 February 2012 в 09:09
поделиться

4 ответа

Старый вопрос, но я обнаружил, что задаю то же самое. Как перехватить рендер рендера? Использование 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();
} );

Я тестировал этот код, используя экспресс 3.0.6. Он должен работать с 4.x без проблем. Вы также можете переопределить определенные комбинации URL с помощью

app.use( '/myspcificurl', function( req, res, next ) {...} );
27
ответ дан 29 November 2019 в 01:37
поделиться

Объект 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);
};
7
ответ дан 29 November 2019 в 01:37
поделиться

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

Как вы, возможно, знаете, 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);
};
12
ответ дан 29 November 2019 в 01:37
поделиться

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

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

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

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;
3
ответ дан 29 November 2019 в 01:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: