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

Я создаю приложение Node.js с Connect / Express.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' });
});

Это выглядит как надуманный пример, но он вписывается в общую структуру, которую я строю.

Мои знания о ООП и наследовании прототипов в 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' });
});

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

Ответы [ 4 ]

25 голосов
/ 05 июня 2014

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

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

Как вы, возможно, знаете, javascript - это язык на основе прототипов, и у каждого объекта есть прототип, например объекты ответа и запроса. Глядя на код ( Экспресс 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);
};
7 голосов
/ 15 февраля 2012

Объект 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 голосов
/ 02 октября 2015

Недавно я обнаружил, что мне нужно сделать то же самое, чтобы предоставить для каждого из моих шаблонов идентификатор свойства 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...