2012-02-14 31 views
21

私はConnect/Express.jsでNode.jsアプリケーションを構築しています。元のレンダリング関数に転送する前にコードを実行するres.render(view、option)関数をインターセプトしたいと思います。Node.js/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の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' }); 
}); 

動作するようです。私が各リクエストのための新しいレスポンスラッパーオブジェクトを作成している最中の解決策であるかどうかは分かりませんが、それは問題でしょうか?

+1

を、通常、あなたがそれを行う方法であること。ルータの前で使用されているミドルウェアに入れておけば、1か所に設定することができます。 –

+0

私はミドルウェアを使うことを考えました。私がアプリケーション全体の応答の振る舞いをオーバーライドしたいのかどうかはわかりません。私のフレームワークを通してルーティングされるリクエストについてのみです。 –

答えて

22

古い質問が、自分が求めてい:

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をハックする方がよいかもしれません同じこと。 resレンダリングをインターセプトするには? Express 4.0xを使用しています。

ミドルウェアを使用/書き込みできます。このコンセプトは、最初は私にはやっかいなものでしたが、読んだ後にはもう少し意味がありました。これを読んだ誰かのためのコンテキストのために、res.renderをオーバーライドする動機は、グローバルなビュー変数を提供することでした。私はsessionを私のすべてのテンプレートで利用できるようにしたいのですが、すべてのresオブジェクトに入力する必要はありません。

基本的なミドルウェア形式は次のとおりです。

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

次のparamと関数呼び出しは、実行するために重要です。 nextは、複数のミドルウェアがブロックせずに自分のことをできるようにするコールバック関数です。より良いexplanation read here

のためにこれは、私は特急3.0.6を使用して、このコードをテストしてみたロジック

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

をレンダリング上書きするために使用することができます。それは問題なく4.xで動作するはずです。 ます。また

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

これは、すべての要求に対して関数をオーバーライドし、GCに圧力をかけますか?プロトタイプをオーバーライドする方が理にかなっていますか? – jocull

+0

これはすべての要求に優先します。プロパティのシャドウイングによってプロトタイプをオーバーライドします。必要に応じてプロトタイプをオーバーライドすることができますが、一般的に、プロトタイプチェーンの深くないメインオブジェクトのプロパティが必要です。プロパティルックアップ時間はこのように高速です。 – Lex

+1

私はこの回答を最初に得ていませんでしたが、自分の周りをいじめると、今すぐ私はそれを得ます。彼のコードのバリエーションをhttps://github.com/mettamage/renderExtMiddlewareでチェックアウトすることができます。私は '_.extend(args)'を使用しません。私のコードでは、クライアントがブラウザでない場合はjsonを返す通常のビュー*以外のレンダリングが行われます。 –

7

responseオブジェクトにはプロトタイプがありません。これは、(ミドルウェアに入れるのライアンのアイデアを取って)動作するはずです:

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

ありがとう。これは、レイアウト/マスタービューもどのように見えるか分かりませんが、 'testViews/layout.ejs'を検索しようとします。私はソースを見て、_renderメソッドがすでにレスポンスオブジェクトに定義されていることを確認しました。これは問題を引き起こす可能性がありますか? –

+0

はい、正しいです - すべてのビューに "testViews /"が付加されます。これは 'ServerResponse._render'とは関係ありません。あなたは何を達成しようとしていますか? –

+0

ServerRespone.prototypeのハックを実装しようとしましたが失敗しました。ノード0.6 /エクスプレッション2で動作すると思いますか?ノード0.8と表現3でこの作業をどうやって行うのか考えていますか? –

6

それは、ミドルウェアはそれぞれ、すべての要求とそれぞれの実行を取得しているため、これらの各インスタンスのレスポンスやリクエストメソッドをオーバーライドするためのミドルウェアを使用するのは良い考えではありませんし、特定のURLのコンボを上書きすることができますそれはあなたが新しい機能を作成しているので、メモリと同様にCPUを使用すると呼ばれます。

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

私はこのコメントが受け入れられた理由ではないのか分かりません!絶対的な正解です! –

3

私は最近、自分自身が私のテンプレートのそれぞれに設定固有のGoogleアナリティクスのプロパティIDとCookieドメインを提供するために、同じことを行う必要が見つかりました。

多くの優れた解決策があります。

私はLexの提案するソリューションに非常に近いものを選びましたが、res.render()の呼び出しには既存のオプションが含まれていないという問題がありました。オプションが未定義たため、例えば、次のコードは、()に延びるように呼び出しで例外を引き起こした:

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

I添加コールバックを含むことが可能である引数の様々な組み合わせ、を占める、以下。他の人が提案したソリューションでも同様のアプローチを使用できます。

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; 
+0

2つのポイントがあります。地元のプロパティはほとんどの場合に適合します。レンダリングにオプションが渡されなければ、コールバックは未定義となり、すべての地獄は緩んでしまいます。 – Lex

関連する問題