またASP.NETでハマり。本格的にいじってるといろいろ出てくる。
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で渡されたパラメータに応じてデータベースから値を取り出して、それをページに埋め込んだりしてるでしょう。キャッシュ期間中はデータベースの値を書き換えてもページに反映されないわけです。
問題はこれから。
この最強に便利なOutputCacheですが、aspxとascxでしか使えません。ashxでは使えないのです。
aspxはページそのもの、ascxはページに埋め込まれるコントロールですが、ashxはそれ以外の場合に使います。たとえば、JavaScriptやCSSやRSSなどの非htmlなテキストデータを返したり、画像などのバイナリデータを返したりするときにも使える。とっても便利。
ところが、ashxではOutputCacheが使えないのです。これはかなり痛い。JavaScriptやCSSやRSSや画像なんて、まさしくOutputCacheを使いたい状況ベスト5に入るんじゃないかと思いますが、ダメなんです。コンパイルすら通らない。
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);
}
こんな感じでしょうか。
上記は文字列(string)の場合の例ですが、バイナリデータならbyteの配列を使って同じように書けると思います。キャッシュのために書いたコードは実質4行ほどなので、それほど複雑化したという感じでもないかと思います。
このCacheは、有効期限以外にもいろんな便利な使い方ができるので、OutputCacheよりもはるかに高機能です。その分使うのがちょっと面倒というトレードオフはあるけど。
自前で実装するのはイヤだ、なんとしても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で作った方がマシだとは思います。