muumoo.jp


ニュース記事検索


過去ログ 2007年07月18日 - ニュース過去ログ

ニュース過去ログ

2007/07/18 管理人日記 ashxではOutputCacheはできない (管理人日記)

またASP.NETでハマり。本格的にいじってるといろいろ出てくる。

OutputCache

ASP.NETのすばらしい特徴のひとつに、OutputCacheがあります。これは、aspxファイルの先頭に

<%@ OutputCache Duration="1" Location="Any" VaryByParam="*" %>

のようなたった1行のコードを書くだけで、出力キャッシュが実装できるというものです。ちなみに上記のコードは1秒キャッシュ(Duration="1"がその意味)。

これは、URLごとに出力するデータをキャッシュするという指定。つまり、1秒以内に同じURLへアクセスが来た場合は、全ての処理をスキップしてさっき出力したデータと同じものを返すということになります。思いっきり環境によりますが、RPS(1秒間に処理できるリクエスト数)は数十倍〜数百倍になることが多い。簡単に実装できるのに非常に強力な機能です。

キャッシュする時間は1秒なんていわずに10秒でも1時間でもいいんですが、キャッシュしてる間はそのページを書き換えることができないので注意。ASP.NETを使ってる以上、何かしら動的なページを出力してるはずです。おそらくは、URLで渡されたパラメータに応じてデータベースから値を取り出して、それをページに埋め込んだりしてるでしょう。キャッシュ期間中はデータベースの値を書き換えてもページに反映されないわけです。

ashxでは使えない

問題はこれから。

この最強に便利なOutputCacheですが、aspxとascxでしか使えません。ashxでは使えないのです。

aspxはページそのもの、ascxはページに埋め込まれるコントロールですが、ashxはそれ以外の場合に使います。たとえば、JavaScriptやCSSやRSSなどの非htmlなテキストデータを返したり、画像などのバイナリデータを返したりするときにも使える。とっても便利。

ところが、ashxではOutputCacheが使えないのです。これはかなり痛い。JavaScriptやCSSやRSSや画像なんて、まさしくOutputCacheを使いたい状況ベスト5に入るんじゃないかと思いますが、ダメなんです。コンパイルすら通らない。

解決策1 : 自分でキャッシュを実装

ashxのメインの処理をするProcessRequestメソッドは、引数としてHttpContextが渡されていて、この中にCacheプロパティがあります。これはデータキャッシュと呼ばれる領域です。

制限時間やその他の条件(あるファイルが変更されるまで、等)を指定して、好きなデータをキャッシュさせることができます。コレを使って、OutputCacheと同等の仕組みを自分で作ってしまえば良いわけです。

public void ProcessRequest (HttpContext context) {
  string url = context.Request.RawUrl;
  string output = (string)context.Cache[url];
  if (output == null)
  {
    /* 出力する文字列を作る処理をここに記述 */
    output = (作った文字列);
    context.Cache.Insert(url, output, null, DateTime.Now.AddSeconds(1), Cache.NoSlidingExpiration);
  }
  context.Response.ContentType = "text/plain";
  context.Response.Write(output);
}

こんな感じでしょうか。

  1. context.Request.RawUrlはリクエストされたURL。
  2. URLをキーにしたキャッシュを取り出してみる。
  3. それがnullだった場合、好きな処理を行い、出力データを作る。
  4. 作ったデータをキャッシュに格納(キーはURL)。有効期限は1秒。
  5. データを出力する。

上記は文字列(string)の場合の例ですが、バイナリデータならbyteの配列を使って同じように書けると思います。キャッシュのために書いたコードは実質4行ほどなので、それほど複雑化したという感じでもないかと思います。

このCacheは、有効期限以外にもいろんな便利な使い方ができるので、OutputCacheよりもはるかに高機能です。その分使うのがちょっと面倒というトレードオフはあるけど。

解決策2 : ムリヤリaspxにしてしまう

自前で実装するのはイヤだ、なんとしてもOutputCacheを使いたい、そんな場合は、もう思い切ってashxをやめてaspxにしてしまえばいいと思います。

<%@ Page Language="C#" %>
<%@ OutputCache Duration="1" Location="Any" VaryByParam="*" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        /* 出力する文字列を作る処理をここに記述 */
        string output = (作った文字列);
        
        Response.ContentType = "text/plain";
        Response.Write(output);
        Response.End();
    }
</script>
<html><body></body></html>

どうでしょうか。

ムリヤリではありますが、やっぱりキャッシュにまつわるコードが全廃できるので、スッキリして良いですね。それは認める。ただ、ダミーでコードを書く点や、aspxなのにページを返してない点など、あとで別な人がこのコードをみたら「なんじゃこりゃ?」となる可能性が高い。

どうしてもこの方法を採用するなら、コメントでちゃんと説明した方がいいと思います。

// 本来こういう処理は ashx として作成すべきところだが、ashx は OutputCache が使えないため、あえて aspx として作成している。html タグや body タグはエラーを避けるために書いているダミーであり、コード内で Response.End() を呼んでいるのでこのタグが出力されることはない。

コメントには理由を書く。ただ、こんな説明をするくらいならashxで作った方がマシだとは思います。

この記事のURI:
http://muumoo.jp/news/2007/07/18/0ashxoutputcache.html


Copyright© 2002-2007 muumoo.jp All Rights Reserved.