2015年5月23日

ASP.NET 出力レスポンスをキャプチャする方法

ブラウザに出力される HTML や API の出力などのレスポンスを、サーバー側で取得したい場合がたまにある。
ASP.NET (not MVC) でこれを行う方法はいくつかある。

Response.Filter を使う方法

Response.Filter にフィルタクラス (Stream 継承) を設定すると、レスポンスを出力前に加工することができる。
加工目的でなくても、Stream.Write に出力内容が入ってくるので、その内容を MemoryStream 等に入れればレスポンスをキャプチャできる。

[キャプチャ用フィルタクラス実装例]

public class CaptureStream : Stream
{
    public CaptureStream(Stream targetStream)
    {
        _targetStream = targetStream;
        Captured = new MemoryStream();
    }
    private readonly Stream _targetStream;
    public MemoryStream Captured { get; private set; }

    public override void Write(byte[] buffer, int offset, int count)
    {
        // 対象のストリームとメモリに書込み
        _targetStream.Write(buffer, offset, count);
        Captured.Write(buffer, offset, count);
    }

    public override void Flush()
    {
        _targetStream.Flush();

        // ここで Captured に対する処理を行う (ログ出力など)
        // OnFlush イベントを用意するとベター
    }

    public override void Close()
    {
        _targetStream.Close();
        Captured.Close();
        base.Close();

        // Captured に対する処理は、ここでも可能
        // MemoryStream は Close してもバッファは残るので ToArray などが可能
    }

    // 残りのオーバーライドは適宜実装
}

//---- 使用例 ----//
// Page クラス内の OnLoad などで
Response.Filter = new CaptureStream(Response.Filter);

デメリットは、キャプチャしたデータにアクセスできる Page イベント(オーバーライド可能メソッドも含む)が無いこと。
つまり、Page ライフサイクル内にキャプチャしたデータを処理するタイミングはなく、上記コードのようにフィルタクラスの Flush ないし Close 時に処理を挟むことしかできない。
そして、Page ライフサイクルから外れるため、セッションに保存できない。

余談だが、Response.Filter は ASP.NET MVC にも存在しているので、同じことができる。

Page.Render を使う方法

Page.Render をオーバーライドすると、出力されるレスポンスデータにアクセスできる。
→ そのままキャプチャ可能。

[Page.Render のキャプチャ実装例]

protected override void Render(HtmlTextWriter writer)
{
    string captured = null;

    using( var sw = new StringWriter() )
    using( var htw = new HtmlTextWriter(sw) ){
        // メモリ内にレスポンス出力
        base.Render(htw);
        captured = sw.ToString();

        // 文字列化したレスポンスをブラウザに出力
        writer.Write(captured);
    }

    // captured に対する処理を行う
}

この方法は、Page ライフサイクル内の処理なので、セッションに保存できる。

デメリットは、オーバーライドなので、フィルタクラスのように機能分離できないこと。

参考URL

0 件のコメント:

コメントを投稿