WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

HTTP ハンドラでキャッシュコントロール

by WebSurfer 2015年5月19日 17:55

このブログでは、アプリケーションのフォルダに保存した画像やスクリプトファイルなどを応答として返すのに HTTP ハンドラを使っています。

F5 キーを押すなどしてページをリロードすると、下の画像のように HTTP 304 Not Modified 応答を返しますが、これを HTTP ハンドラで実現するにはどのようにするかという話をメインに、HTTP ハンドラを利用したキャッシュコントロールについて書きます。

HTTP 304 応答

HTTP ハンドラを使わないで、以下のように直接 img 要素の src 属性に画像ファイルの URL を設定した場合は、IIS が ETag, Last-Modified に設定する値を当該ファイルから取得し、それらを応答ヘッダに含めて返してくれます。

<img alt="" src="/images/sample2.jpg" />

クライアントが F5 キーなどでページをリロードすると、再度ブラウザが src 属性に設定された URL の画像を要求しますが、そのとき If-Modified-Since と If-None-Match を要求ヘッダに含めて送ってくれます。

要求を受けた IIS は要求ヘッダに含まれる If-Modified-Since と If-None-Match を見て、条件を満たせば(即ち、画像が変更されてなければ) HTTP 304 Not Modified 応答を返します(ヘッダのみでコンテンツは送信しない。ブラウザのキャッシュを使えという指示と同じ)。

ちなみに、ブラウザのキャッシュを削除すると、画像を要求する際 If-Modified-Since と If-None-Match は要求ヘッダには含まれなくなます。なので、キャッシュを削除してから F5 キーを押せば、IIS は画像のコンテンツを含めて HTTP 200 応答を返します。

src 属性に HTTP ハンドラを指定する(HTTP ハンドラ経由でファイルをダウンロードする)場合は、IIS が自動的に行ってくれる上記の処置を、自力で HTTP ハンドラにコーディングすることになります。

簡単に書くと、(1) ブラウザ送られてくる要求ヘッダの中の If-None-Match, If-Modified-Since の値を取得し、その値に応じて HTTP 200 応答(コンテンツを含む)または HTTP 304 応答(コンテンツ無し)のどちらかを返す。(2) 要求を受けたら毎回、応答ヘッダの ETag, Last-Modified に設定する値を当該ファイルから取得し、それらを応答ヘッダに含めてブラウザへ返す・・・という処置をコーディングすることになります。

さらに、せっかく HTTP ハンドラを使って細かくキャッシュコントロールのためのコードが書けるのですから、上記 (1), (2) だけでなく(ETag, Last Modified の応答ヘッダへの設定だけでなく)、Cache-Control, Expires 等の応答ヘッダへの設定も行って、プロキシやブラウザのキャッシュをきちんとコントロールした方がよさそうです。

また、サーバー側でのキャッシュもコントロールすることが可能になりますので、サーバーのキャッシュが利用できればそれを使わない手はないですよね。

ということで、例として、画像ファイル用とスクリプトファイル用それぞれの HTTP ハンドラを、Web サイトプロジェクトの ジェネリックハンドラ(.ashx ファイル)の形で書いたコードを以下に紹介します。ベースは BlogEngine 1.6.1.0 のものです。

(1) 画像用の HTTP ハンドラ

<%@ WebHandler Language="C#" Class="_0114_ImageHandler" %>

using System;
using System.Web;
using System.IO;
using System.Net;

public class _0114_ImageHandler : IHttpHandler {
    
  public void ProcessRequest (HttpContext context) 
  {
    HttpResponse response = context.Response;
    HttpRequest request = context.Request;
        
    // ファイル名はクエリ文字列で指定。
    string fileName = request.QueryString["picture"];
        
    // 画像ファイルはアプリケーションルート直下の
    // images という名前のフォルダにあることが前提。
    string folder = "~/images/";

    if (!String.IsNullOrEmpty(fileName))
    {            
      string path = 
          context.Server.MapPath(folder + fileName);
      FileInfo fileInfo = new FileInfo(path);
      if (fileInfo.Exists)
      {            
        // BlogEngine では何故か作成日時 (CreationTimeUtc) が
        // 使われていたが、IIS が設定するのと同様に、更新日時
        // (LastWriteTimeUtc) を使用するように変更。
        DateTime lastWriteTime = fileInfo.LastWriteTimeUtc;

        // ETag は更新日時のタイマ刻み数。(IIS とは異なる)
        string etag = "\"" + lastWriteTime.Ticks + "\"";
        string ifNoneMatch = request.Headers["If-None-Match"];

        DateTime ifModifiedSince = DateTime.MinValue;
        DateTime.TryParse(
            request.Headers["If-Modified-Since"], 
            out ifModifiedSince);

        response.Clear();
        // ブラウザとプロキシにキャッシュを許可
        response.Cache.SetCacheability(HttpCacheability.Public);
        // 有効期限を、要求を受けた日時から 1 年に設定。
        response.Cache.SetExpires(DateTime.Now.AddYears(1));
        // Last-Modified を応答ヘッダに設定。
        response.Cache.SetLastModified(lastWriteTime);
        // ETag を応答ヘッダに設定。
        response.Cache.SetETag(etag);

        // 要求ヘッダの If-None-Match, If-Modified-Since と、
        // 上のコードで取得した etag, lastWriteTime を比較し、
        // どちらかが一致したら HTTP 304 応答を返して終了。
        if (ifNoneMatch == etag || 
            ifModifiedSince == lastWriteTime)
        {
          response.StatusCode = 
              (int)HttpStatusCode.NotModified;
          return;
        }

        int index = fileName.LastIndexOf(".") + 1;
        string extension = 
            fileName.Substring(index).ToLowerInvariant();

        // IE は image/jpg を認識しないことへの対応
        if (extension == "jpg")
        {
          context.Response.ContentType = "image/jpeg";
        }
        else
        {
          context.Response.ContentType = "image/" + extension;
        }
                
        response.TransmitFile(fileInfo.FullName);
      }
    }
  }
 
  public bool IsReusable 
  {
    get 
    {
      return false;
    }
  }
}

ETag, Last Modified, Expires の違いについては以下の stackoverflow の記事が参考になると思います。

(2) スクリプト用の HTTP ハンドラ

<%@ WebHandler Language="C#" Class="_0114_ScriptHandler" %>

using System;
using System.Web;
using System.IO;
using System.Security;
using System.Web.Caching;
using System.Net;

public class _0114_ScriptHandler : IHttpHandler 
{
    
  public void ProcessRequest (HttpContext context) 
  {
    HttpResponse response = context.Response;
    HttpRequest request = context.Request;

    // ファイル  名はクエリ文字列で指定。
    string fileName = request.QueryString["filename"];

    // スクリプトファイルはアプリケーションルート直下の
    // scripts と言う名前のフォルダにあることが前提。
    string folder = "~/scripts/";

    if (!String.IsNullOrEmpty(fileName))
    {
      string script = null;
            
      // まずサーバーのCacheをチェック。なければファイルに
      // アクセスしてスクリプトを取得。
      if (context.Cache[fileName] == null)
      {
        if (!fileName.EndsWith(".js",
            StringComparison.OrdinalIgnoreCase))
        {
          throw new SecurityException("アクセス不可");
        }

        string path =
            context.Server.MapPath(folder + fileName);
        FileInfo fileInfo = new FileInfo(path);

        if (fileInfo.Exists)
        {
          using (StreamReader reader = 
                              new StreamReader(path))
          {
            script = reader.ReadToEnd();
                        
            // Minify は割愛。
                        
            // サーバーのキャッシュに保持。
            context.Cache.Insert(
                        fileName,
                        script,
                        new CacheDependency(path));
          }
        }
      }
      else
      {
        script = (string)context.Cache[fileName];
      }

      if (!string.IsNullOrEmpty(script))
      {
        response.Clear();
        response.ContentType = "text/javascript";

        // Vary: Accept-Encoding をヘッダに設定。
        response.Cache.VaryByHeaders["Accept-Encoding"] = true;

        // 有効期限 (Expires ヘッダ) を 7 日に設定。
        response.Cache.SetExpires(
                DateTime.Now.ToUniversalTime().AddDays(7));
        // Cache-Control ヘッダの max-age を 7 日(604800 秒)
        // に設定。
        response.Cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
        // Cache-Control ヘッダに must-revalidate を設定。
        response.Cache.SetRevalidation(
                HttpCacheRevalidation.AllCaches);

        int hash = script.GetHashCode();
                
        string etag = "\"" + hash.ToString() + "\"";
        string ifNoneMatch = request.Headers["If-None-Match"];
                
        // ETag を応答ヘッダに設定。
        response.Cache.SetETag(etag);
        // Cache-Control ヘッダに public を設定(ブラウザと
        // プロキシにキャッシュを許可)
        response.Cache.SetCacheability(HttpCacheability.Public);
                
        // 要求ヘッダの If-None-Match と上のコードで取得した
        // etag を比較し一致したら HTTP 304 応答を返して終了。
        // サーバーの Cache を利用する関係で Last Modified
        // は使用しない。
        if (ifNoneMatch == etag)
        {
          response.StatusCode = 
                      (int)HttpStatusCode.NotModified;
          return;
        }

        response.Write(script);
      }            
    }
  }
 
  public bool IsReusable 
  {
    get 
    {
      return false;
    }
  }
}

ファイルから取得したスクリプトの文字列をサーバーのキャッシュに保持するようにし、2 回目以降の要求ではキャッシュから取得するようにしています。

キャッシュから取得する場合はスクリプトファイルの更新日時を取得できないので、Last Modified は使わず ETag のみを応答ヘッダに含めています。

ETag の値は、スクリプトの文字列から String.GetHashCode メソッドでハッシュコードを取得し、それを設定しています。

Tags:

Cache

aspx ページでのリソースの利用

by WebSurfer 2015年4月21日 21:22

カスタムコントロールにおけるリソースの使い方は、先の記事 多言語対応カスタムコンロトール に書きました。今回は .aspx ページでリソースを使う場合について書きます。

リソースファイル

ほとんどのことは以下のページ(後者の方が詳しいですが前者のほうが文章が平易で分かりやすいかも)に書いてありますので、わざわざここで書く必要もないかもしれませんが、自分が気がついた注意点などを書いておきます。

Web サイトプロジェクトの場合、リソースファイルは以下の 3 つの場所に配置できます。(Web アプリケーションプロジェクトでは、それに加えてプロジェクト内の任意の場所に置くことができます)

  1. App_GlobalResources フォルダ
  2. App_LocalResources フォルダ
  3. 独立したリソースアセンブリ

上のそれぞれについて、以下に備忘録的なことを書いておきます。

(1) App_GlobalResources フォルダ

Web アプリ全体で利用するためのリソースファイルは、アプリケーションルート直下に App_GlobalResources フォルダを作りその中に配置します。

例として、上の画像のように、MyResource.resx という名前のリソースファイルを配置し、それに Welcome という key 名で文字列を保存することを考えます。(上の画像には BookTitle というのもありますが)

Web アプリケーションプロジェクトでは App_GlobalResources フォルダにリソースファイルを追加すると、xxx.Designer.cs という名前(xxx は拡張子無しのリソースファイル名)の「厳密に型指定されたリソースクラス」が自動的に生成されますが、Web サイトプロジェクトの App_GlobalResources フォルダにリソースファイルを追加した場合はそうならない点に注意してください。

しかしながら、Visual Studio からは見えないものの、見えないところで Resources という名前空間に MyResource という「厳密に型指定されたリソースクラス」が定義されます。

従って、以下の画像のようにコードを入力する際にインテリセンスが働いて、定義済みの候補(画像の例では Resources 名前空間にある MyResource クラス)が表示されます。

インテリセンスによる表示

key 名の Welcome はプロパティとして定義されているので、それに該当するリソースは Resources.MyResource.Welcome というようにして取得できます。

上記の方法の他に、How to: Retrieve Resource Values Programmatically に書かれていますように、GetGlobalResourceObject メソッドでもリソースを取得できます。

下の (2) に書いた HTTP ハンドラのように、一つの共通プログラムでリソース名と key 名を切り替えて画像を取得するような場合は、「厳密に型指定されたリソースクラス」より GetGlobalResourceObject メソッドを使うのがよさそうです。

さらに、上に紹介した URL の記事にあるとおり、.aspx ページに直接 $Resources 式を埋め込んで取得することもできます。文字列を取得するならそれが一番簡単で、本筋だと思われます。

(2) App_LocalResources フォルダ

特定のページで利用するリソースファイルは、そのページのあるフォルダ下に App_LocalResources フォルダを作ってその中に配置します。

リソースファイルの名前の付け方には制約があって、例えば、Default.aspx というページから利用するリソースファイルの名前は Default.aspx.resx となります。

(マイクロソフト公式解説書「プログラミング Microsoft ASP.NET 4」の p.315 によると リソースファイルの名前にフォルダ名をつけると、フォルダレベルで適用可能となるリソースファイルになるとのことです。ただし、自分が試した限りですが、$Resources 式や meta 属性を使って取得できないなど、あまり利用価値はなさそうなのでその話は割愛します)

上の名付けルールにより、特定のリソースファイルを特定の .aspx ページに関連付けているようです。例えば、Default.aspx ページで $Resources 式を使う場合、Text="<% $Resources:Welcome %>" とすれば Default.aspx.resx リソースファイルの中の Welcome という key 名のリソースを取得できます。

(逆に、Text="<% $Resources:Default.aspx,Welcome %>" のようにすると、グローバルリソースを探しに行くらしく、リソースが見つからないというエラーになります)

プログラムでリソースを取得することもできます。その場合、GetLocalResourceObject メソッドを使用します。

自分が試した限りですが、HttpContext クラスの HttpContext.GetLocalResourceObject メソッド を使うと、第 1 引数に .aspx ページの仮想パス(例えば下の画像のようなフォルダ構造で、Default.aspx.resx の中のリソースを取得したい場合は "~/TestFolder/Default.aspx")、第 2 引数にリソースの key 名を指定して、特定のページ以外のどこからでも、どこにあるローカルリソースでも取得できるようです。

ただ、ほとんどのケースでは .aspx ページから直接 $Resource 式や meta 属性を使って取得すれば済むはずで、どのようなケースでプログラムで取得するような必要があるかが疑問です。

自分が思いつくのはリソースが画像の場合ぐらいです。

リソースファイルの画像

例えば、上の画像のように Jpeg などの画像ファイルがリソースに含まれていて、それを ImageButton に表示するというような場合です。

その場合は $Resources 式を使って ImageUrl="<% $Resources:JpegSample %>" のようにしてもダメです。html にレンダリングされると src="System.Drawing.Bitmap" となってしまいます。

ImageUrl プロパティは html ソースでは src 属性になり、それに設定された url をブラウザが Web サーバーに要求に行くので、url 参照で取得できるようにしなければなりません。

それには HTTP ジェネリックハンドラを利用できます。上の画像にある ResourceImageHandler.ashx を以下のように定義して、それを ImageButton の ImageUrl に設定すれば OK です。

<%@ WebHandler Language="C#" Class="_ResourceImageHandler" %>

using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;

public class _ResourceImageHandler : IHttpHandler 
{    
  public void ProcessRequest (HttpContext context) 
  {
    string rsc = context.Request.QueryString["resource"];
    string key = context.Request.QueryString["key"];

    if (!String.IsNullOrEmpty(rsc) && 
        !String.IsNullOrEmpty(key))
    {
      Bitmap bmp = (Bitmap)HttpContext.
          GetLocalResourceObject("~/TestFolder/" + rsc, key);

      if (bmp != null)
      {
        context.Response.
            Cache.SetCacheability(HttpCacheability.Public);
        context.Response.
            Cache.SetExpires(DateTime.Now.AddYears(1));

        context.Response.ContentType = "image/jpeg";
        context.Response.Clear();

        bmp.Save(context.Response.OutputStream, 
                                    ImageFormat.Jpeg);
      }
    }
  }
 
  public bool IsReusable 
  {
    get 
    {
      return false;
    }
  }
}

(3) 独立したリソースアセンブリ

この方法を取ることはまずないと思いますが・・・

Web アプリとは別にプロジェクトを追加し、その中にリソースファイルを配置することも可能です。

例として、ClassLibrary2 という名前で新しいプロジェクトをソリューションに追加し、そのプロジェクトに Resource1.resx という名前のアセンブリリソースファイルを追加することを考えます。

Visual Studio で Resource1.resx を開くと、画面の右上に[アクセス修飾子(I):]がドロップダウン式で選択できるようになっていますが、それを Public にするのを忘れないようにしてください(デフォルトは Internal)。

自動生成される Resource1.Designer.cs ファイルの中に、名前空間名 ClassLibrary2 下に厳密に型指定されたリソースクラス Resource1 が定義されます。

リソースファイルに文字列、テキストファイル、画像などを保存すると、Resource1 クラスに、保存したリソースを取得するためのプロパティが自動的に設定されます。

従って、Web アプリで ClassLibrary2 を参照設定しておけば(そうすると、ソリューションをビルドした時、Web アプリの Bin フォルダに ClassLibrary2.dll が自動的に配置されます)、Web アプリのコードからは ClassLibrary2.Resource1.<プロパティ名> でリソースファイルに保存したリソースを取得できます。

なお、自分の環境で検証した限りですが、GetGlobalResourceObject メソッドや $Resources 式ではリソースを取得できないので注意してください。

-------- 2016/6/17 追記 --------

サテライトアセンブリを追加して多言語対応する場合は Culture, UICulture を "auto" に設定するのを忘れないようにしてください。

Culture, UICulture を "auto" に設定すると、ASP.NET は、ブラウザから送信されてくる要求ヘッダに含まれる Accept-Language の設定を調べて、その要求を処理するスレッドのカルチャを Accept-Language に設定されているカルチャに書き換えます。

そして、リソースマネージャが実行時に、Thread.CurrentUICulture などで得られる CultureInfo(現在の要求を処理しているスレッドのカルチャ情報)を参照してローカライズされたリソースを検索し、UI に表示されるテキストを取得するという仕組みになっています。

Culture, UICulture を "auto" に設定するのを忘れるとブラウザの言語設定は無視されます。デフォ��トではシステムのロケールに該当するカルチャがスレッドに設定されますので、例えば日本語 OS で xxx.ja-JP.resx というリソースがあれば、常にそれから UI に表示されるテキストを取得します。

Web サイトが日本語専用でサーバーも日本にあれば忘れても問題ないかもしれませんが、ホスティングサービス(Azure も含む)でサーバーが外国にある場合は Culture, UICulture を "auto" に設定するのを忘れると問題が出ると思います。

Tags: , ,

ASP.NET

ASP.NET MVC でトレース情報の表示

by WebSurfer 2015年4月6日 21:52

ブラウザからトレースビューア Trace.axd を要求したとき表示される[トレース情報]に、System.Diagnostics.Trace クラス(Page.Trace プロパティで取得できる TraceContext オブジェクトではない点に注意)を利用して書き込んだトレースメッセージを表示する方法を書きます。

トレースビューアの表示

この記事の例では、一つのソリューションの中に二つのプロジェクトがあって、一方が ASP.NET MVC4 の Web アプリケーション、他方がデータベースなどからデータなどを取得する中間ビジネス層のクラスライブラリを考えます。また、View に Razor 構文を使用することとします。

ASP.NET Web Forms アプリケーションの例は MSDN ライブラリの チュートリアル : ASP.NET トレースと System.Diagnostics トレースの統合 に詳しく書いてあります。(現時点の記事には、「Web フォームのトレース メッセージを書き込むには」のセクションのコード Trace.WriteLine が Trace.Write の間違い、WebPageTraceListener とコンパイラのバージョンが古いなどの問題があるので注意してください)

これと同様なことを MVC4 Razor 構文のアプリケーションで行ってみます。ちなみに、この記事の検証に使った環境は、Vista SP2, ASP.NET 4, VS2010 Professional, MVC4 インターネットアプリケーションテンプレートで作成、IIS7 統合パイプラインモード上で実行、ブラウザは IE9 です。

(1) クラスライブラリ(Model 相当)

チュートリアルにある AuthorClass ビジネスオブジェクトに相当するクラスライブラリは、以下のコードのとおり実装しました。

MVC アプリケーションの Model として利用しやすいように、Author クラスを定義し、List<Author> 型のオブジェクトを作成して渡すようにしています。チュートリアルとは違って、Authors.xml ファイルは使用していません。

トレースメッセージは、チュートリアルと同様に、AuthorClass オブジェクトの生成時点と AuthorClass.GetAuthors メソッドの呼び出し時点で Trace.Write メソッドを使って書き込みます。

加えて、非同期で呼び出したメソッドでトレースメッセージを書き込んだ場合にはどうなるかを検証するため、AsynchronousMethod を追加し、非同期で呼び出してみました。(結果は、上の画像の通りトレースビューアには表示されません)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AuthorClassLibrary
{
  public class Author
  {
    public string au_id { get; set; }
    public string au_lname { get; set; }
    public string au_fname { get; set; }
    public string au_phone { get; set; }
  }

  public delegate Author WriteTraceMessage();
    
  public class AuthorClass
  {        
    public AuthorClass()
    {
        // トレースメッセージ書込み
        System.Diagnostics.Trace.Write(
            "AuthorClass is created.", "AUTHORCLASS TRACE");
    }

    public List<Author> GetAuthors()
    {
      // トレースメッセージ書込み
      System.Diagnostics.Trace.Write(
          "GetAuthors called.", "AUTHORCLASS TRACE");
            
      List<Author> authors = new List<Author>() {
        new Author { au_id = "172-32-1176", au_lname = "White",
            au_fname = "Gerry", au_phone = "408 496-7223" },
        new Author { au_id = "172-32-1176", au_lname = "Green",
            au_fname = "Marjorie", au_phone = "415 986-7020" }
      };

      // AsynchronousMethod を非同期呼び出し
      WriteTraceMessage asyncCall = 
          new WriteTraceMessage(AsynchronousMethod);
      IAsyncResult ar = asyncCall.BeginInvoke(null, null);
      Author author = asyncCall.EndInvoke(ar);
      authors.Add(author);

      return authors;
    }

    // 非同期で呼び出すメソッド
    static Author AsynchronousMethod()
    {
      // トレースメッセージ書込み(Trace.axd では表示されない)
      System.Diagnostics.Trace.Write(
          "AsynchronousMethod called.", "AUTHORCLASS TRACE");

      Author author = new Author() {
          au_id = "123-45-6789", au_lname = "太郎",
          au_fname = "日本", au_phone = "000 111-2222" };

      return author;
    }
  }
}

(2) Controller

MVC4 インターネットアプリケーションテンプレートで自動生成される HomeController に、以下のように Authors アクションメソッドを追加します。

ブラウザから Home/Authors が呼び出されたときに Trace.Write メソッドを使ってトレースメッセージを書き込むようにしています。

また、このメソッドの中で、上のクラスライブラリの AuthorClass コンストラクタと AuthorClass.GetAuthors メソッドが呼び出され、トレースメッセージが書き込まれることに注意してください。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Mvc4App.Controllers
{
  public class HomeController : Controller
  {

    // ・・・中略・・・

    public ActionResult Authors()
    {
      System.Diagnostics.Trace.Write(
        "Home/Authors method called.", "CONTROLLER TRACE");
            
      ViewBag.Message = "System.Diagnostics Trace の統合。";

      AuthorClassLibrary.AuthorClass authors = 
                new AuthorClassLibrary.AuthorClass();
      return View(authors.GetAuthors());
    }
  }
}

(3) View

View においても、以下のコードのように、Trace.Write メソッドを使ってトレースメッセージを書き込むことができます。

@model IEnumerable<AuthorClassLibrary.Author>

@{
    ViewBag.Title = "ASP.NET MVC Trace";
    Layout = "~/Views/Shared/_Layout.cshtml";
    
    System.Diagnostics.Trace.Write(
        "Home/Authors.cshtml called.", "VIEW TRACE");
}

<hgroup class="title">
    <h1>@ViewBag.Title.</h1>
    <h2>@ViewBag.Message</h2>
</hgroup>

<table>
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.au_id)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.au_lname)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.au_fname)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.au_phone)
        </td>        
    </tr>
}
</table>

Trace.Write メソッドで書き込んだトレースメッセージが出力されるようにするには、Visual Studio で当該プロジェクトの Property を開き、その[ビルド]タブの[TRACE 定数の定義(T)]にチェックマークを入れる必要があります。

[トレース定数の定義]の設定

View については更なる設定が web.config に必要です(上の画像の[トレース定数の定義]の設定だけでは View の Trace.Write メソッドは無視されます)。

具体的には、C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config の中の complier 要素を、アプリケーションルート直下の web.config にコピーして、それに compilerOptions="/define:TRACE" を追加します。

詳しくは、MSDN ブログの記事 Tracing in ASP.NET MVC Razor Views を見てください。チュートリアルの「トレースを有効にしてアプリケーションを自動コンパイルするには」のセクションにも同様なことが書かれていますが、こちらはバージョンが古いので注意してください。

今回の検証で使った環境では以下のようになります。

<system.codedom>
    <compilers>
        <compiler 
            language="c#;cs;csharp" 
            extension=".cs" 
            warningLevel="4" 
            compilerOptions="/define:TRACE" 
            type="Microsoft.CSharp.CSharpCodeProvider, 
                System, 
                Version=4.0.0.0, 
                Culture=neutral, 
                PublicKeyToken=b77a5c561934e089">
            <providerOption 
                name="CompilerVersion" 
                value="v4.0"/>
            <providerOption 
                name="WarnAsError" 
                value="false"/>
        </compiler>
    </compilers>
</system.codedom>

さらに、System.Diagnostics.Trace クラスを使用して書き込んだトレースメッセージをルーティングして、ASP.NET トレース ビューア(Trace.axd)に表示されるようにするには、web.config で trace 要素を有効にし、WebPageTraceListener を追加する必要があります。

WebPageTraceListener の設定はチュートリアルの「構成でアプリケーションの WebPageTraceListener を追加するには」のセクションに書かれていますが、バージョンが古いようです。

WebPageTraceListener は System.Web.dll に含まれていますので、参照設定を見てそれと同じバージョンにするのがよさそうです。具体的には、今回の検証で使った環境では、以下の通りです。

<system.web>
    <trace enabled="true" pageOutput="true" 
        requestLimit="40" localOnly="false"/>
</system.web>

<system.diagnostics>
    <trace>
        <listeners>
            <add 
                name="WebPageTraceListener"
                type="System.Web.WebPageTraceListener, 
                System.Web, 
                Version=4.0.0.0, 
                Culture=neutral, 
                PublicKeyToken=b03f5f7f11d50a3a"/>
        </listeners>
    </trace>
</system.diagnostics>

上記の通り設定してブラウザから Home/Authors を呼び出すと以下のように表示されるはずです。

Home/Authors の表示

その後、ブラウザからトレースビューア Trace.axd を呼び出し、ブラウザに表示された Home/Authors 行の[詳細の表示]をクリックして[トレース情報]を見ると一番上の画像のようになっているはずです。

非同期呼び出しした AsynchronousMethod メソッドで書き込んだトレースメッセージ以外はトレースビューアに表示されているのがわかるでしょうか?

Tags: ,

MVC

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年5月  >>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar