JavaScript

クッキー

    /// <summary>
    /// このクラスはクッキーをハッシュで管理します。
    /// </summary>
    function CookieContainer()
    {
	    // ローカル変数は関数の中からしか参照できない
	    // 関数オブジェクトのプロパティであり、
	    // プライベート・フィールドのように扱うことができます。

	    // 関数もまたオブジェクトであり、ローカル変数と同様、
	    // ローカル関数として利用でき、
	    // プライベート・メソッドのように扱うことができます。


	    /// <summary>
	    /// Objectクラスのインスタンスをハッシュとして使用します。
	    /// </summary>
	    var _hashValues = _getHashValues();


	    // サブ・ドメインを超えてクッキーを扱う場合には、
	    // URLのホスト名に「.」+ドメイン名が
	    // 含まれているかどうか調べ、
	    // 含まれている場合にクッキーのドメインを変更します。

	    /// <summary>
	    /// ターゲットのドメインにマッチしたかどうかを保存します。
	    /// </summary>
	    var _matched = location.hostname.match(/\.(hesperus\.net)$/i);


	    /// <summary>
	    /// クッキーから値を取得します。
	    /// </summary>
	    /// <param name="cookieName">クッキー名</param>
	    /// <returns>
	    /// 指定された名前のクッキーの値を返します。
	    /// クッキーがなければundefinedを返します。
	    /// </returns>
	    this.getValue = function (cookieName) {
		    return _hashValues[cookieName];
	    }


	    /// <summary>
	    /// 指定された名前で値をクッキーに保存します。
	    /// </summary>
	    /// <param name="cookieName">クッキー名</param>
	    /// <param name="cookieValue">保存する値</param>
	    /// <returns>
	    /// 保存したクッキーの値を返します。
	    /// </returns>
	    this.setValue = function (cookieName, cookieValue) {

		    // クッキー名には「;」「=」を含めることが出来ませんが、
		    // スペースを含めることは出来ます。
		    // クッキー名の前後にあるホワイトスペースは
		    // 取り除かれるようです。

		    // クッキー値には「;」を含めることは出来ませんが、
		    // 「=」とスペースを含めることは出来ます。
		    // クッキー名同様、クッキー値の前後にある
		    // ホワイトスペースは取り除かれるようです。

		    // with (document) {
		    // 	cookie = " a b c   =   def  = g h i  ;klmn ";
		    // 	write("\"" + cookie + "\"");
		    // }
		    //
		    //	---- 結果 ----
		    //
		    //	"a b c=def  = g h i"

		    // クッキー名、クッキー値ともにエンコードしておきます。

		    var cookieText = escape(cookieName) + "=" + escape(cookieValue);


		    // ドメイン属性の指定

		    // ドメイン属性の指定は、サブドメインを超えて
		    // クッキーを扱う場合に必要になります。
		    // そうでなければ暗にURLのホスト名が
		    // 使われるのと同じになるので、
		    // 指定する必要はありません。

		    // ドメインを超えてクッキーを扱うことはできません。
		    // これはクッキーの基本的な事項です。
		    // ただ「co.jp」などは、形式的には
		    // ドメイン名として認識できそうな気がするので、
		    // もしかすると「hoge.co.jp」や「fuga.co.jp」の間で
		    // クッキーを共有することは可能かもしれません。
		    // 仮にこれができてしまったとしたら、
		    // もちろんドメイン名に「co.jp」を含む
		    // 他のドメインでもクッキーを参照できてしまい、
		    // 影響を及ぼしたりして
		    // セキュリティ的に問題がありそうですから、
		    // できないことにしておいたほうが良さそうです。

		    // サブドメインを超えてクッキーを扱う場合でも、
		    // ホストとドメインが一致しているときは
		    // 変更する必要はありません。

		    if (_matched) {
			    // 関連のないドメインを設定しようとすると失敗します。
			    // 失敗させたいときは、いつでも設定するようにし、
			    // 失敗させたくない(既定のドメインで設定する)場合は
			    // ドメインの設定をしないようにします。
			    // 通常、関連のないドメインは他のサイトから
			    // 利用されている場合に限られるので、
			    // 無駄なコストをかけなくても良いです。

			    cookieText += "; domain=" + _matched[1];
		    }


		    // パス属性の設定

		    // 一般にサブ・ドメインはそれぞれまったく異なる構成なので、
		    // サブ・ドメインを越えてクッキーを扱う場合、
		    // クッキーのパスは「/」(ルート)にしておいたほうが便利です。

		    // クッキーは「path=」で設定したディレクトリ以下で
		    // 参照することができます。
		    // 例えば「path=/images」としておけば
		    // 「/images」以下の全てのディレクトリで有効です。

		    // クッキーの話はクライアント側のことなので
		    // 正確に言うとディレクトリと言うよりは
		    // 多分「URLのパス」のことです。
		    // サーバ側に実際のディレクトリがなくても
		    // URLのパス部分がpathinfoなどでパスのような形をしていれば
		    // パスと認識されます。
		    //
		    // http://www.hesperus.net/default.aspx/pathinfo/index.html
		    //
		    // クライアント側でURLからpathinfoを取り出す一般的な方法は
		    // ありません。人間なら目視で「default.aspx」はプログラムと
		    // わかるのですが、それがディレクトリなのかコンテンツなのかを
		    // ブラウザはURLからは判断できないのです。

		    // パスを指定しなければ、
		    // クッキーは現在のパス以下でのみ参照できます。

		    // パスを変えれば同じ名前で重複するように
		    // 保存することもできますが、
		    // 通常、これは混乱を招く結果に終わります。

		    // サイトがサーバで使用するホーム・ディレクトリに相当する
		    // パスを設定するのが無難です。
		    // 例えば「http://www.hesperus.net/~kaigo/」を与えられ、
		    // これを独立したサイトとして運用するなら、
		    // パスは「/~kaigo」としておきます。

		    cookieText += "; path=/";


		    // 有効期限属性の指定

		    // 他のブラウザではきちんと試していませんが、
		    // とりあえずIE6では、下のコードでクッキーの期限を
		    // 50ヶ月先の日付にすることが出来ます。

		    // var expires = new Date();
		    // expires.setMonth(expires.getMonth() + 50);
		    // cookieText += "; expires=" + expires.toGMTString()

		    // 期限を現在の日時より前にすると、
		    // クッキーを削除することが出来ます。

		    // expires.setDate(expires.getDate() - 1);

		    // たまにサーバの時刻とクライアントの時刻が
		    // 違っていたりすることがあるので、
		    // サーバ側からクッキーを削除するときは
		    // 多めに考えておいたほうが良いようです。
		    // 例えば1年前の日付にするなど。


		    // クッキーに保存

		    document.cookie = cookieText;


		    // ハッシュに保存

		    return _hashValues[cookieName] = cookieValue;
	    }


	    /// <summary>
	    /// クッキーを読み取りハッシュに納めて返します。
	    /// </summary>
	    /// <returns>
	    /// 読み取ったクッキーを納めたハッシュを返します。
	    /// </returns>
	    function _getHashValues() {

		    // 戻り値
		    var result = new Object();


		    // document.cookieには「; 」(セミコロンとスペース)を
		    // 区切り文字として現在のセッションとパスに関連した
		    // 0個以上のクッキーが含まれています。
		    // (クライアント側ではクッキーのドメインやパスや有効期限を
		    // 見ることは出来ません。)

		    var fields = document.cookie.split(/;\s+/);


		    // 区切り文字で区切った各フィールドから
		    // クッキーの名前と値を順次取り出します。

		    for (var i = 0; i < fields.length; i++) {

			    // 各クッキーを最初の「=」で2つに分けます。

			    // ヒットしなければString.match()の戻り値はnullです。
			    // ヒットすれば次のようなプロパティを持った
			    // オブジェクトを返してきます。

			    // index  	ヒットした文字列のインデックス。
			    //		文字列の先頭でヒットすれば0。
			    // input  	被検索文字列。上の場合はfields[i]。
			    // [0]		マッチした部分文字列。
			    //		上の場合は「name=value」。
			    // [1],[2],...	検索文字列で指定した()に対応する
			    //		部分文字列。上の場合は[1]に最初の
			    //		「=」の前の部分、[2]に最初の「=」の
			    //		後ろの部分が入る。

			    var matched = fields[i].match(/(.*?)=(.*)/);

			    if (matched) {

				    // クッキーの名前と値を取り出します。

				    // 「setValue()」で保存するときに
				    // 両方ともエンコードしてあるはずなので
				    // ここでは両方ともデコードします。

				    var cookieName = unescape(matched[1]);
				    var cookieValue = unescape(matched[2]);


				    // ハッシュに保存します。

				    result[cookieName] = cookieValue;
			    }
		    }


		    // 戻り値を返します。

		    return result;
	    }
    }


    // クッキー・コンテナ・クラスのインスタンスを作成します。
    // クッキー・コンテナ・クラスのインスタンスはページを通じて1つあれば足ります。

    cookies = new CookieContainer();


    // 使用例

    // 「myCookieName」という名前のクッキーの値を取り出し、ページに表示します。
    document.write(cookies.getValue("myCookieName"));

    // 値「Hello World!\n」を「myCookieName」という名前でクッキーに保存します。
    cookies.setValue("myCookieName", "Hello World!\n");

    // 既存の関数を置き換える場合
    function getCookieValue(cookieName) {
	    return cookies.getValue(cookieName);
    }

    function setCookieValue(cookieName, cookieValue) {
	    return cookies.setValue(cookieName, cookieValue);
    }

仕様

  • クラス名
    • CookieContainer
  • コンストラクタ
    • CookieContainer()
      • 引数
        • (なし)
      • 説明
        • コンストラクタを呼んでクラスのインスタンスを生成すると、全てのクッキーの名前と値が読み取られます。
  • プライベート・プロパティ(*1)
    • _hashValues
      • 説明
        • クッキー名をキーにしたクッキー値のハッシュとして使用します。
  • プライベート・メソッド(*1)
    • _getHashValues()
      • 引数
        • (なし)
      • 戻り値
        • 読み取ったクッキーを納めたハッシュを返します。
      • 説明
        • document.cookieから全てのクッキーを読み取ります。
  • パブリック・メソッド
    • getValue(cookieName)
      • 引数
        • cookieName
          • クッキー名を指定します。
      • 戻り値
        • 指定された名前のクッキーの値を返します。クッキーがなければ「undefined」を返します。
      • 説明
        • ハッシュに保存されたクッキー値を返します。
    • setValue(cookieName, cookieValue)
      • 引数
        • cookieName
          • クッキー名を指定します。
        • cookieValue
          • クッキーに設定する値を指定します。
      • 戻り値
        • 設定したクッキーの値を返します。
      • 説明
        • 値をクッキーに保存します。パス属性はルート「/」です。ドメイン属性には適宜「hesperus.net」を指定します。有効期限はありません(セッションに保存されます)。secure属性は指定しません。

(*1) 通常、プライベート・メンバの記述は必要ありません。

サーバ側で処理しているクッキーをクライアント側では扱わないほうが良いようです。逆にクライアント側で作成し扱うクッキーをサーバ側では処理しないほうが良いです。encodeURIComponent()やdecodeURIComponent()等を使えばUnicodeなので可能かなと思いましたが、クライアント側では文字セットの扱いが完全にブラウザに依存しており、ブラウザ内部で扱われている文字セットとUnicodeとの間の変換をサポートするメソッドは(知らないだけかもしれませんが)見つからないからです。サーバ側でのエンコード/デコード<-->エンコード値(クッキー)<-->クライアント側でのエンコード/デコードといった変換が普通にできてしまうと、セキュリティ的に問題があるのかもしれません。

それから、unescape()はエラーを発生させずに安全に使えますが、decodeURIComponent()のほうは適切な引数を指定しないとエラーを発生させ、かつ実行するまでエラーが起こるかどうかわからないため、例外処理が必要になります。