web-dev-qa-db-ja.com

Node.js / Express.js-res.render関数をオーバーライド/インターセプトする方法は?

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

不自然な例のように見えますが、私が構築している全体的なフレームワークに適合します。

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

うまくいくようです。リクエストごとに新しい応答ラッパーオブジェクトを作成しているので、それが最善の解決策であるかどうかわからない場合、それは問題になりますか?

24
Sunday Ironfoot

古い質問ですが、同じことを尋ねていることに気づきました。 resレンダリングをインターセプトする方法は? Express4.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 ) {...} );
27
Lex

ミドルウェアはリクエストごとに実行され、呼び出されるたびにメモリだけでなく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);
};
13
Ali

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

最近、各テンプレートに構成固有の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();
});

edit:実際にコードを実行する必要がある場合、これはすべて便利かもしれませんが、私がやろうとしていたことを達成するための非常に簡単な方法があることがわかりました。 Expressのソースとドキュメントをもう一度確認したところ、app.localsがすべてのテンプレートのレンダリングに使用されていることがわかりました。したがって、私の場合、最終的に上記のすべてのミドルウェアコードを次の割り当てに置き換えました。

app.locals.gaPropertyID = config.googleAnalytics.propertyID;
app.locals.gaCookieDomain = config.googleAnalytics.cookieDomain;
3
Timothy Johns