Quantcast
Channel: monoe's blog
Viewing all 51 articles
Browse latest View live

IIS7 ".NETユーザー"にパスワード変更の自由を。

$
0
0

以前、ブログに書いた通り IIS7 では、Web サイトや仮想ディレクトリの認証に ASP.NET Web フォーム認証を使用することができます。

その際、IIS7 の管理ツールを使用して、".NET ユーザー" アカウントを作成するのですが、用意されている UI では、アカウントの作成と削除/メールアドレスの変更しかできません。

[操作] パネルには、そのほかに [パスワードのリセット] というリンクが用意されておりますが、これをクリックすると既定では以下のような、メッセージボックスが表示され、実質アカウントのパスワードのリセット作業を行うことはできません。

これでは アカウントを使用しているユーザーが、パスワードを変更したくなった場合や、失念してしまった場合は、管理者いちいちアカウントを削除して作り直す必要があり面倒ですし、そもそもユーザーが自分で使用するアカウントを管理者が知っているというのはセキュリティ面からも健全ではありませんし、なにしろ気持ちの良いものではありません。

今回は IIS7 の機能としてはあまり紹介されていない、というか、そんなネタはどこにも載っていない、ある意味 "幻の" と冠をつけてもいいような .NET ユーザーが、自らのパスワードを、自らのために、自らの手によって変更することが可能になる方法をご紹介しましょう。

そして、この方法を実行することで、前出のメッセージボックスに表示されているメッセージの内容に対する以下の "謎" が、たちどころに氷解することでしょう。(勿体ぶっててサーセンw)

  1. ユーザーの"質問" と "回答" ってなに?
  2. "ユーザーが自身がパスワードをリセットするようにしてください"って、具体的にどーすんの?

.NET ユーザーが、自らのパスワードを変更可能とするためにはパスワード変更用のページを作成する必要があります。しかし、プログラムのコーディングを行う必要はありません。

具体的な手順は、以下の通りです。

.NET ユーザーのパスワード変更ページの作成
================================
テキストエディタに以下のタグを貼り付け、changePassword.aspx と名前を付けて保存します。

<html>

<head runat="server">

    <title></title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:ChangePassword ID="ChangePassword1" runat="server"

            ContinueDestinationPageUrl="~/default.htm">

        </asp:ChangePassword>

    </div>

    </form>

</body>

</html> 

Web フォーム認証が正常に動作している IIS7 の Web サイトに配置し、ブラウザからアクセスします。

認証画面を経て、パスワードの変更画面が表示されるので必要な項目を入力し、 [パスワードの変更] ボタンをクリックすると現在使用している .NET ユーザー アカウントのパスワードが変更されます。

ASP.NET のメンバシップ機能を使用したことがある方であれば、ピンときたと思うのですが、パスワード変更に表示される UI は ASP.NET Web フォームコントロールの ChangePassword コントロールです。

これは Web フォーム認証に Login コントロールが使用できる理由と同じく、 IIS7 と ASP.NET のパイプラインが統合されているためにこういったことが可能になるのです。

よって、パスワード変更画面の詳細な設定については、以下のドキュメントに紹介されているプロパティを <asp:ChangePassword> タグの属性として追加することで可能になります。

『ChangePassword プロパティ』
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.changepassword_properties(VS.80).aspx

あとのデザインについては、自らの感性に任せてタグなり CSS なりを追加しまくっちゃってください。:-P

 

Web Statistics

IIS7 ".NET ユーザー" によるパスワードのリセット

$
0
0

前回の記事で IIS7 の Web フォーム認証で使用する ".NET ユーザー アカウントのパスワードの変更方法" について書きましたが、今回はパスワードのリセット方法について書きたいと思います。

ところで自らパスワードをリセットしなければならないシチュエーションとはいつでしょう?

多くの場合は、パスワードを忘れてしまった場合です。

人は忘却の生き物です。日々、さまざまなことを忘れていきます。

若いころの情熱や夢、かつて強く信じていた大切なもの、連れ合いの誕生日や記念日、そしてなによりも大事なパスワードさえも、いつのまにか気持ちよく忘れてしまうものなのです。

パスワードを忘れてしまったばあい、多くは管理者にパスワードのリセットを依頼する必要があります。

つまりは、他人の余計な仕事を増やしたうえに、リセット作業が完了するまで自らの人生の貴重な時間を無駄に過ごすことになりがちなのです。

IIS7 がサポートする .NET のメンバシップには、パスワードの失念に因るこういった多大なる損失を回避するための機能が備えられています。

それが .NET ユーザーのための、.NET ユーザーの手による、.NET ユーザーのパスワードリセット機能なのです。

この機能は、".NET ユーザー" アカウント作成の際に設定した "質問" に、おなじく設定した "回答" を入力することで、パスワードを新しいパスワードで上書きし、さらには、その新しいパスワードをアカウント作成時設定したメールアドレスに送ってくれるという、なんとも親切心にあふれた機能なのです。

(と、ここまで書いていて、前回の記事で書いた "前出のメッセージボックスに表示されているメッセージの内容に対する以下の "謎" が、たちどころに氷解" というのは、この機能のことだと気が付いたしだい)

この機能の実装も、パスワードの変更機能と同じく、ノンコーディングで実装できます。

具体的な手順は以下の通りです。

.NET ユーザーのリセットページの作成
================================
テキストエディタに以下のタグを貼り付け、PasswordRecovery.aspx と名前を付けて保存し、Web フォーム認証が設定されている IIS7 の Web サイト/仮想ディレクトリに配置します。

<html>
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:PasswordRecovery ID="PasswordRecovery1" runat="server">
        </asp:PasswordRecovery>
    </div>
    </form>
</body>
</html>

次に、Web.config にメールサーバーの設定を行います。

使用可能な SMTP サーバーがある場合はweb.config の <system.net> 内に以下の設定を行います。

<mailSettings>
  <smtp from="送信元メールアドレス">
    <network
  host="SMTPサーバー名"
  userName="メールサーバーのユーザー名"
  password="パスワード" />
  </smtp>
</mailSettings>

使用できる SMTP サーバーがない場合は、メールをピックアップディレクトリに保存できるよう、以下のように設定を行ってください。

<mailSettings>
  <smtp
    deliveryMethod="SpecifiedPickupDirectory"
    from="送信元メールアドレス">
    <specifiedPickupDirectory
      pickupDirectoryLocation="ピックアップディレクトリのパス" />
  </smtp>
</mailSettings>

次に IIS7 の設定を行います。

IIS 管理ツールをオープンし、作業を行っている Web サイト、あるいは仮想ディレクトリを選択し、[ 機能ビュー ] 内の [ SMTP 電子メール ] アイコンをダブルクリックします。

SMTP サーバーを指定する設定画面が表示されますので、使用可能な SMTP サーバーがある場合は、以下のドキュメントを参考に必要事項を設定します。

『IIS 7.0: SMTP 電子メールを構成する』
http://technet.microsoft.com/ja-jp/library/cc772058(WS.10).aspx

使用可能な SMTP がない場合は、以下のようにピックアップディレクトリ(※)を指定します。
※任意のフォルダで結構です。

設定が完了したら配置した PasswordRecovery.aspx にブラウザからアクセスします。

認証ページが表示されるかもしれませんが、対処方法は後述しますので、そのままログインして PasswordRecovery.aspx を表示してください。

画面に以下の画面が表示されるので、パスワードのリセット作業を行いたいユーザー名を入力して [ 送信 ] ボタンをクリックします。

次にパスワードリセットのための質問が表示されますので回答を入力し、[ 送信 ] ボタンをクリックすると、パスワードをリセットした旨のメールが送信されます。

送信されたメールの内容の確認は、SMTP サーバーを指定した方は、届いたメールの内容を確認してください。

ピックアップディレクトリを指定した方は、ピックアップディレクトリとして指定したフォルダにエクスプローラーでアクセスし、保存されている *.eml ファイルをダブルクリックしてオープンしてください。

以下の内容が確認できます。

件名 : パスワード
サイトに戻って、次の情報を使用してログインしてください。
ユーザー名: user01
パスワード: gH0^pyb%0+NfB%

メールの内容にあるパスワードでログインが可能確認してください。

 

今回のパスワードのリセット機能に使用したのは ASP.NET メンバシップに用意されている Web フォームコントロール の PasswordRecovery コントロールです。

詳細な設定については、以下のドキュメントに紹介されているプロパティを <asp:PasswordRecovery > タグの属性として追加することで可能になります。

『PasswordRecovery プロパティ』
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.passwordrecovery_properties(VS.80).aspx

 

Web サイトのルートに認証が設定されている場合の対処方法

トップレベルに認証が設定されている Web サイト内に、前出のパスワードリセット用のページを配置した場合、そのページにアクセスするにはサイトにログインする必要があるため、パスワードを忘れた状態ではアクセスすることはできません。

この状況への一つの問題解決策として考えられるのは、承認が済んでいなくても唯一アクセス可能なページである、ログインページにパスワードリセットページを兼用させるという方法です。

具体的には、以下のようなタグを使用してログインページを作成します。

<%@ Page Language="C#" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>

<script runat="server">
    static bool vlu = false;
    protected void PasswordRecovery1_VerifyingUser(object sender, LoginCancelEventArgs e) {vlu = true;}
</script>

<script type="text/JavaScript">
function visibleForm(flg) {
    if (document.all('status').value == "True") flg = true;
  document.all('forget').style.display= (flg)?"block":"none";
  document.all('login').style.display= (flg)?"none":"block";
  document.all('status').value = flg;
}
</script>

</head>
<body onload="visibleForm(false)" >
    <form id="form1" runat="server">
     <div id="login" style="display:block;">   
        <asp:Login ID="Login1" runat="server">
        </asp:Login>
<br />
パスワードを忘れた方は <a href="JavaScript:visibleForm(true);">こちら</a> をクリック
</div>
    <div id="forget" style="display:none;">
    <asp:PasswordRecovery ID="PasswordRecovery1"  runat="server"
                onverifyinguser="PasswordRecovery1_VerifyingUser"  />       
<br />
ログイン画面に <a href="JavaScript:visibleForm(false);">戻る</a>
    </div>
    <input type="hidden" id="status" value="<%=vlu%>" />
    </form>
</body>
</html>

このページでは、ログインページ内に表示される “パスワードを忘れた方は こちら をクリック” の “こちら” のリンク部分をクリックすると、パスワードリセット用の UI に切り替わり、そのままリセット処理を行うことができます。

 

さて、このパスワードのリセット機能の手軽さと便利さには感心することしきりなのですが、1 つだけ疑問が残ります。

それは、パスワードのリセット作業の際に使用する "質問" と "回答" についてです。

これらの設定は、".NET ユーザー" アカウントの作成時に管理者によって行われます。

と、いうことは、".NET ユーザー" アカウントを管理する管理者は、各ユーザーの "お母さんの旧姓" とか、"初めてのペットの名前" を把握しておく必要があるということでしょうか?

どうやら、 IT に関して "管理者" と呼ばれる職種の仕事は増え続ける一方のようです。 :-P


Web Statistics

IIS7 ".NET ユーザー" アカウントをブラウザから作成するには?

$
0
0

既に多くの方がご存じのとおり、IIS7 では ASP.NET Web フォーム認証が、Web サイトの標準的な認証方式として使用することが可能となっています。

当然、Web フォーム認証で使用する .NET ユーザー アカウントも IIS7 の管理ツールで作成可能です。

この .NET アカウントの作成には、アカウントを使用しているユーザーがパスワードを失念してしまった場合に自らパスワードのリセットを行うための "質問" と "回答" を設定する必要があります。

しかし、通常の場合、IIS 管理ツールを使用してこの作業を行うのは Web サイトの管理者です。

つまり、この状況では、先日の記事で書いたように、Web 管理者は、アカウントを払い出す各々のユーザーの "お母さんの旧姓" とか、"最初のペットの名前" などを把握している必要があるのです。

もしかしたら私の見識が狭いだけなのかもしれませんが、すべての Web サーバー管理者が、各ユーザーに対し、"ねぇ、キミが最初に飼ったペットの名前を教えてくれるかな? ちなみにうちは団地だったからペットは飼ったことがないんだよね、テヘっ" なんてフレンドリーに訊ける状況というのはなかなかないような気がします。

とくに "お母さんの旧姓" なんてのは、訊く人によってはシリアスな問題をはらんでいそうなので、通常業務にスリルを持ち込みたいという向こう見ずな冒険心でも抱かないかぎりはなかなか気が進まないところです。

では、"質問" と "回答" は、管理者が用意したものを使用し、忘れん坊のユーザーがパスワードを失念した際には、リセット作業を行えば良い、という意見もあるでしょう。

一見これは優れたアイディアに見えますが、数々のアカウント作成、パスワードのリセット作業を繰り返していくなかで、管理者はやがてこう思うことでしょう。

いったい誰のための "質問" と "回答" なんだよ? と。

また、.NET ユーザー アカウントは、Web サイト / アプリケーション ごとに独立して作成されているので、それらすべてに対してのアカウント作成からバスワードのリセットまでの作業を担うというのは、管理者にしてみればなかなか笑顔ではいられない状況です。

たとえば、数十個の Web アプリケーションで .NET Web フォーム認証を使用しており、それぞれに対し数十個のユーザーアカウントをいちいち手動で作成していく行為は、それによって悟りを得ようとでもしていないかぎりはなかなかつらいものがあります。

しかも、その Web アプリケーションが業務の基幹を担うような重要なものであるならまだしも、なんら収益に結びつかないレクリエーション的な用途であれば、管理者は、自らのモチベーションの低下を食い止めるための様々な工夫を講じるはめになるでしょう。

この問題を解決するにはどうすればいいでしょう?

一つの方法として考えられるのは、ユーザーに、自ら使用するユーザーアカウントを作成させるという方法です。

現在、さまざまな会員制のサイトや、Web のメールアプリケーションで採られている方式です。

これによって管理者は些末なユーザーアカウントの作成作業から解放されるでしょう。

今後、管理者はユーザーアカウントを求めるユーザーにはこう返答するだけでいいのです。

"You、作っちゃいなよ!" と。

 

そうです、IIS7 には、ユーザーに自らのアカウントを生成させるための仕組みが用意されているのです。

その具体的な手順は以下の通りです。

 

.NET ユーザー アカウントの作成ページ
=========================
テキストエディタに以下のタグを貼り付け、CreateUser.aspx と名前を付けて保存し、Web フォーム認証が設定されている IIS7 の Web サイト/仮想ディレクトリに配置します。

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:CreateUserWizard ID="CreateUserWizard1" runat="server"
        ContinueDestinationPageUrl="default.htm" Question="最初のペットの名前">
        <WizardSteps>
            <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
            </asp:CreateUserWizardStep>
            <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
            </asp:CompleteWizardStep>
        </WizardSteps>
    </asp:CreateUserWizard>
    </form>
</body>
</html>

ただし、このページはログインアカウントを持っていないユーザーにアクセスさせる必要があるので、配置する仮想ディレクトリは "匿名ユーザー" に対しアクセスの許可を設定しておく必要があります。

この作業は IIS 管理ツールの [ 承認規則 ] を使用して設定を行います。(次回の記事で詳細します)

ブラウザから CreateUser.aspx にアクセスすると、以下のようなアカウント作成画面が表示されるので、各項目を設定し、[ユーザーの作成] ボタンをクリックするとアカウントが作成されます。

アカウントが作成されたら、作成したアカウントでログインが可能かどうか確認してください。

 

今回のパスワードのリセット機能に使用したのは ASP.NET メンバシップに用意されている Web フォームコントロール の CreateUserWizard コントロールです。

詳細な設定については、以下のドキュメントに紹介されているプロパティを <asp:CreateUserWizard> タグの属性として追加することで可能になります。

『CreateUserWizard プロパティ』
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.createuserwizard_properties.aspx

 

付録 : CreateUserWizard コントロールのカスタマイズ

.NET ユーザー アカウントの作成 UI を提供する CreateUserWizard コントロールをカスタマイズして、任意の情報、つまりは、氏名や住所、課金情報などを入力させる画面を表示させ、情報を取得することができます。

具体的な設定例を以下に紹介します。

 

.NET ユーザーアカウント作成画面に住所氏名の入力画面を追加する
=============================================
テキストエディタに以下のタグを貼り付け、CreateUser2.aspx と名前を付けて保存し、Web フォーム認証が設定されている IIS7 の Web サイト/仮想ディレクトリに配置します。

<%@ Page Language="C#" %>
<script runat="server">

protected void CreateUserWizard1_CreatedUser1(object sender, EventArgs e)
{
    ProfileCommon prof = (ProfileCommon)ProfileCommon.Create(CreateUserWizard1.UserName, true);

    prof.SetPropertyValue("firstName", firstName.Text);
    prof.SetPropertyValue("lastName", lastName.Text);
    prof.SetPropertyValue("zip", zip.Text);
    prof.SetPropertyValue("state", lastName.Text);
    prof.SetPropertyValue("city", city.Text);
    prof.SetPropertyValue("address1", address1.Text);
    prof.SetPropertyValue("address2", address2.Text);
    prof.SetPropertyValue("phone", phone.Text);
    prof.Save();
}
</script>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type ="text/css">
        div.caption {float: left;}
        div.input {float: left;}
        input.state{width:100px;}
        input.zip{width:100px;}
        input.address{width:360px;}
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <asp:CreateUserWizard ID="CreateUserWizard1" runat="server"
            ContinueDestinationPageUrl="~/default.htm"
            oncreateduser="CreateUserWizard1_CreatedUser1">
            <WizardSteps>
           
            <asp:wizardstep ID="Wizardstep1" runat="server" steptype="Start" title="Identification">
                個人情報を入力してください:<br />
                <div>
                    <div class="caption">姓:</div>
                    <div class="input">
                        <asp:textbox id="lastName" runat="server" />
                        <asp:RequiredFieldValidator runat="server"
                            ID="RequiredFieldValidator1"
                            ControlToValidate="lastName"
                            ErrorMessage="*" />
                    </div>
  
                    <div  class="caption">名:</div>
                    <div class="input">
                        <asp:textbox id="firstName" runat="server" />
                        <asp:RequiredFieldValidator runat="server"
                            ID="RequiredFieldValidator2"
                            ControlToValidate="firstName"
                            ErrorMessage="*" />
                    </div>
                </div>
  <div style="clear:both;">
                    <div class="caption">〒:</div>
                    <div class="input">
                        <asp:textbox id="zip" runat="server" CssClass="zip" />
                    </div>
                </div>
  <div style="clear:both;">
                    <div class="caption">都道府県:</div>
                    <div class="input">
                        <asp:textbox id="state" runat="server" CssClass="state" />&nbsp;
                    </div>
          
                    <div class="caption">市区:</div>
                    <div class="input">
                        <asp:textbox id="city" runat="server" CssClass="state" />
                    </div>
                </div>
  <div style="clear:both;">
                    <div class="caption">住所1:</div>
                    <div class="input">
                        <asp:textbox id="address1" runat="server" CssClass="address" />
                    </div>
                </div>
  <div style="clear:both;">
                    <div class="caption">住所2:</div>
                    <div class="input">
                        <asp:textbox id="address2" runat="server" CssClass="address"/>
                    </div>
                 </div>
  <div style="clear:both;">
                    <div class="caption">電話番号:</div>
                    <div class="input">
                        <asp:textbox id="phone" runat="server" />
                    </div>
                 </div>
              </asp:wizardstep>
           
                <asp:CreateUserWizardStep ID="CreateUserWizardStep1" runat="server">
                </asp:CreateUserWizardStep>
                <asp:CompleteWizardStep ID="CompleteWizardStep1" runat="server">
                </asp:CompleteWizardStep>
            </WizardSteps>
        </asp:CreateUserWizard>
    </form>
</body>
</html>

次に、ユーザーのプロファイル(フィール?) を設定します。

Web.config ファイルをオープンし、<system.web> 内に以下の設定を記述してください。

<profile>
      <properties>
        <add name="firstName" />
        <add name="lastName" />
        <add name="zip" />
        <add name="state" />
        <add name="city" />
        <add name="address1" />
        <add name="address2" />
        <add name="phone" />
      </properties>
    </profile>

設定は以上で完了です。

ブラウザから CreateUser2.aspx にアクセスすると以下の、個人情報を入力するための画面が表示されます。

なにも入力せずに [次へ] ボタンをクリックすると、項目 [姓] と [名] に必須入力のバリデーションが動作し、赤い * (アスタリスク) が表示されるのを確認してください。

次に、すべての入力項目を埋め、アカウント作成作業を完了します。

このアカウント作成作業に入力して、氏名、住所などの情報は、ASP.NET メンバシッププロバイダが使用している DB 内の aspnet_Profile というテーブルに、独自の妙な形式で保存されています。

この情報を表示させるには、テキストエディタに以下のタグを貼り付け userInfo.aspx という名前で保存し、Web フォーム認証が設定されている IIS7 の Web サイト/仮想ディレクトリに配置します。

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">

    string firstName;
    string lastName;
    string zip;
    string state;
    string city;
    string address1;
    string address2;
    string phone;
   
    protected void Page_Load(object sender, EventArgs e)
    {
        //CSS 攻撃回避のため HTML エンコードは忘れずに
        firstName = HttpUtility.HtmlEncode(Profile.firstName);
        lastName = HttpUtility.HtmlEncode(Profile.lastName);
        zip = HttpUtility.HtmlEncode(Profile.zip);
        state = HttpUtility.HtmlEncode(Profile.state);
        city = HttpUtility.HtmlEncode(Profile.city);
        address1 = HttpUtility.HtmlEncode(Profile.address1);
        address2 = HttpUtility.HtmlEncode(Profile.address2);
        phone = HttpUtility.HtmlEncode(Profile.phone);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style type="text/css" >
        td.caption{ background-color:gainsboro;text-align:right; }
        td.value{ background-color:lemonchiffon; }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    現在のログインアカウント:
    <asp:LoginName ID="LoginName1" runat="server" />
   
    <table>
    <tr><td class="caption" >
    姓:
    </td><td class="value">
        <%=lastName%>
    </td></tr>
     <tr><td class="caption">
    名:
    </td><td  class="value">
        <%=firstName%>
    </td></tr>
    <tr><td  class="caption">
    〒:
    </td><td  class="value">
        <%=zip%>
    </td></tr>
    <tr><td  class="caption">
    都道府県:
    </td><td class="value">
        <%=state%>
    </td></tr>
    <tr><td  class="caption">
    市区:
    </td><td class="value">
        <%=city%>
    </td></tr>
     <tr><td  class="caption">
    住所1:
    </td><td class="value">
        <%=address1%>
    </td></tr>
     <tr><td class="caption">
    住所2:
    </td><td class="value">
        <%=address2%>
    </td></tr>
     <tr><td  class="caption">
    電話番号:
    </td><td class="value">
        <%=phone%>
    </td></tr>
    </table>
    </form>
</body>
</html>

個人情報を入力して作成したユーザーアカウントでログインし、userInfo.aspx にアクセスすると、設定した情報が表示されます。

ちなみに、"住所"、"氏名" などの追加した���報については、コーディングが必要になりますが、任意の DB に格納することも可能です。

<参考>
『カスタム プロファイル プロバイダを使用した Web ユーザーの管理』
http://msdn.microsoft.com/ja-jp/magazine/cc163457.aspx

『CreateUserWizard コントロールのマルチステッププロセス化』
http://japan.internet.com/developer/20060829/26.html

 

次回は、.NET ユーザー アカウントを使用したアクセス権の設定について書きます。

 

 

Web Statistics

IIS7 Web フォーム認証を使用したアクセス権の設定

$
0
0

IIS 7 では、ASP.NET Web フォーム認証が Web サイトの標準的な認証方式して使用できるようになっています

Web フォーム認証が提供しているのは、一見、ログイン際の認証/承認の機能だけのように見えますが、実は下位ディレクトリ各々に対する "アクセス制御" の機能も提供しています。

"アクセス制御" の機能を使用すると、ログインが完了したユーザーアカウントの中から、さらに"見せたい人にだけ見せる" ということが可能になります。

この機能はさまざまなことに利用できます。

たとえば、管理者専用のディレクトリを用意して管理用ページを配置したり、会社で使用するのであれば、部署ごとにディレクトリを用意して、セキュアなグループ専用ページを配置したり、コンシューマー向けのサービスであれば、プレミアム会員向けのディレクトリを用意して、お宝コンテンツを配置したり、といったことが可能です。

この"アクセス制御" は、.NET ユーザーアカウントごとの個別設定も可能ですが、"役割" (ASP.NET では "ロール" と表される) というセキュリティグループ的なものを利用して、複数のユーザーアカウントをグループ単位でまとめて管理するといったことも可能です。

今回はこの "アクセス制御" の設定方法をご紹介します。

 

ユーザーアカウントへのアクセス制御

Web フォーム認証が正常に動作している Web サイトにおいては、[承認規則] の設定のみで、ユーザーアカウントごとのアクセスのアクセスの制御を行うことができます。

設定手順は以下のとおりです。

  1. IIS 管理ツールを起動
  2. IIS 管理ツール画面左のツリービューを展開し、Web フォーム認証が適用されているサイトの内の、目的のディレクトリを選択
  3. 画面中央の[機能ビュー] から[承認規則] アイコンをダブルクリックして[承認規則] の設定画面を表示

  4. [承認規則]のリストで、"すべてのユーザー" に対する "許可" の設定を選択し、画面右の [操作] パネルの [削除] リンクをクリックして削除
  5. [操作] パネルの [許可規則の追加] リンクをクリック
  6. [許可の承認規則の追加] ダイアログボックスが表示されるので、[指定されたユーザー] オプションボタンにチェックをつけ、テキストボックスにアクセスを許可する .NET ユーザーアカウントを入力

  7. [OK] ボタンをクリック
  8. [承認規則] のリストに、"匿名ユーザー" に対する "拒否" の設定があるか確認し、なければ画面右の [操作] パネルの [拒否規則の追加] リンクをクリックして "すべての匿名ユーザー" に対する拒否規則を追加

以上でディレクトリに対する、ユーザーアカウント個別のアクセス制御設定は完了です。

コンテンツへのアクセス制御の効果を確認するために、アクセス制御を設定したディレクトリには適当なコンテンツを配置してください。

適当なコンテンツが思いつかない場合は、この記事で紹介している、ユーザーアカウント作成用のページ createUser.aspx を配置してください。

検証のために、アクセス許可を設定した .NET ユーザーアカウントとは別のアカウントを使用して Web サイトにログインし、アクセス制御を設定したディレクトリ内のコンテンツにアクセスしてください。

アクセス制御が正常に機能していれば、ログインページにリダイレクトされるので、今度はアクセスを許可した .NET ユーザー アカウントでログインし、目的のコンテンツが表示されるかを確認してください。

 

"役割" 単位でのアクセス制御の設定

IIS 7 に用意されている Web フォーム認証では、Windows におけるセキュリティグループのように、複数のユーザーアカウントとまとめた "役割" という機能を持っています。

この "役割" を使用したアクセス制御を行うには、まず "役割" を作成する必要があります。

今回は、イメージしやすいように "SiteAdmins" という、Web サイト管理者用の役割を作成し、Web サイト内の "adminTools" というディレクトリで、同役割に対してのみアクセスを許可する、というシナリオでその手順を紹介させていただきます。

"役割" の作成

"SiteAdmins" という "役割" を作成して、.NET ユーザーアカウントを "役割" に追加するには以下の手順で作業を行います。

  1. IIS 管理ツールを起動
  2. IIS 管理ツール画面左のツリービューを展開し、Web フォーム認証が適用されているサイトを選択
  3. 画面中央の [機能 ビュー] より、[.NET ユーザー] アイコンをダブルクリック
  4. [.NET ユーザー] の設定画面が表示されるので、画面右の [操作] パネルより [役割] リンクをクリック
  5. [.NET の役割] 画面が表示されるので、画面右の [操作] パネルより [追加] リンクをクリック
  6. [.NET 役割を追加] ダイアログボックスが表示されるので、[名前] テキストボックスに "SiteAdmins" と入力して [OK] ボタンをクリック

  7. IIS 管理ツール のウィンドウ左上端にある "戻るボタン" (IE についているのと同じもの) をクリックし、[.NET ユーザー] のリスト画面に戻る
  8. アカウントリストから、作成した役割 "SiteAdmins" に追加する アカウントを選択し、画面右の [操作] パネルより [編集] リンクをクリック
  9. [.NET ユーザーの編集] ダイアログボックスが表示されるので、 [役割] リストにある "SiteAdmins" にチェックをつけ [OK] ボタンをクリック

以上で、"役割"  の作成と、"役割" への .NET ユーザーアカウントの追加は完了です。

 

"役割" によるアクセス権の設定

"役割" に対してディレクトリの設定方法は、前出の .NET ユーザーアカウントとほぼ同じです。

具体的な設定手順は以下のとおりです。

(※) 準備として Web サイト内に "adminTools" というディレクトリを作成し、検証に使用する createUser.aspx ファイルを配置しておいてください、

  1. IIS 管理ツールを起動
  2. IIS 管理ツール画面左のツリービューを展開し、Web フォーム認証が適用されているサイトの内の、目的のディレクトリを選択
  3. 画面中央の[機能ビュー] から[承認規則] アイコンをダブルクリックして[承認規則] の設定画面を表示
  4. [承認規則]のリストで、"すべてのユーザー" に対する "許可" の設定を選択し、画面右の [操作] パネルの [削除] リンクをクリックして削除
  5. [操作] パネルの [許可規則の追加] リンクをクリック
  6. [許可の承認規則の追加] ダイアログボックスが表示されるので、[役割またはユーザーグループの指定] オプションボタンにチェックをつけ、テキストボックスに作成した役割 "SiteAdmins" を入力
  7. [OK] ボタンをクリック
  8. [承認規則] のリストに、"匿名ユーザー" に対する "拒否" の設定があるか確認し、なければ画面右の [操作] パネルの [拒否規則の追加] リンクをクリックして "すべての匿名ユーザー" に対する拒否規則を追加

以上で "役割" に対するアクセス制御の設定は完了ですが、Web フォーム認証を適用した際に Web.conffig の <modules> に設定をしたのと同じく、静的なファイルに対してアクセス制御が有効になるように、簡単な"おまじない" が必要になります。

下位互換のために既定で設定されている RoleManager を削除し、あらためてアセンブリ System.Web.Security.RoleManagerModule で登録しなおすという作業です。

具体的には、Web.config ファイルの <system.webServer> の <modules> に以下の設定を追加します。

<remove name="RoleManager" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />

よって、Web フォーム認証が設定されているサイトの Web.config 内の <modules> の設定は、以下のようになります。

<modules>
   <remove name="FormsAuthentication" />
   <remove name="RoleManager" />
   <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
   <add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
</modules>

以上で "役割" を使用したディレクトリに対するアクセス権の設定は完了です。

検証のために、役割 siteAdmins に参加していない .NET ユーザーアカウントを使用して Web サイトにログインし、adminTools ディレクトリ内の createUser.aspx ファイルにアクセスしてください。

正しくアクセス制御機能が動作していれば、ログインページにリダイレクトされるので、今度は、役割 siteAdmins に参加している .NET ユーザーアカウントでログインを行ってください。

createUser.aspx が表示されれば OKです。

 

このように .NET ユーザーによる認証は、権限のスコープが Web サイト内で完結しているとはいえ、必要にして十分なアクセス制御機能を提供しています。

この認証方式は、IIS7 の管理サービスを使用した共有 Web ホスティングサービスでも、自由かつ、安全にユーザーに使用されることができる唯一のものです。

また、Windows においては、基本認証よりも安全で(※)、かつすべてのブラウザに対して使用可能ですので、基本認証を使用しなければならない明確な理由がない場合は、Web フォーム認証を検討してみるのも良いかもしれません。

※なにせコンピューターアカウントを作成する必要がないので。ただし、ASP.NET コードが動作するので、そのへんは心に留めておく必要があります。

 

Web Statistics

IIS7 の機能を拡張してみる-Hot Link (直リン) 禁止モジュール

$
0
0

IIS7 はパイプラインの変更により、.NET を使用してその機能を拡張することができます。

.NET を使用した IIS 7 の機能拡張については、詳しいセッションを過去に何度か行ったことがあるのですが、この機能に関して���記事をあまり見かけないので書いておきます。

.NET を使用した IIS 7 の拡張方法は大別すると、以下の方向性に大別されます。

  • リクエスト、レスポンスの応答処理
  • IIS 7 に対する操作
  • IIS 管理ツールの拡張

今回は リクエスト、レスポンスの応答処理の中の、リクエストを処理を使用して Hot Link を禁止する方法をご紹介しましょう。

ちなみに Hot Link とは、Web サイトで公開しているコンテンツを、他の Web サイトが直接リンクして使用する行為です。"直リン" などと短縮して呼ばれることもあります。

"コンテンツへの直接のリンク" ということで、"Web に公開しているものに直接リンクしてなにが悪い?" と、疑問に思われる方もいらっしゃることてしょう。

たとえば、自分の Web サイトで、お宝画像、もとい、ユーザーから需要のある画像等を公開し、そのページに貼ったバナーのアフィリエイトで収益を上げているとしましょう。

ところが、他の Web サイトが、自分のサイトで公開している画像を <img> タグなどを使用して、さもその Web サイトが提供しているように公開していたらどうでしょう?

"非常に腹立たしい"、のはもちろんなのですが、なんといっても他所のサイトの訪問者は、バナーの貼ってある自分のサイトのページにはアクセスしませんので、アフィリエイトが機能せず、経済的な損失も生じることになります。

そういった場合には、Hot Link をしている Web サイトに対して注意、警告する必要が出てくるわけですが、運営者が気弱だったり、相手が道徳心に欠けるような ならず者 だったりすると、たいがいの場合、泣き寝入りすることになります。

ご存じのとおり、泣き寝入り という行為は、忍耐力の育成方法として考えてもあまり健全なものではありません。

では、このような問題を解決するにはどうしたらよいでしょう?

はたして何らかの解決策があったとして、"すべてもとどおり" というふうになるでしょうか?

じつは、問題というものは、解決策が必要になった時点で、ある意味すでに手遅れなのです。

"問題の解決策" より、先んじて考えなければならないのは、"問題を発生させない方法" なのです。

そもそも問題が発生しなければ、解決策は必要ありません。

つまり、この Hot Link 問題について、もっとも良い解決策というのは、あらかじめ Hot Link されないようにしておくということです。

ご存じのとおり、問題というものは常に、あらかじめ最初から "存在"している" ものなのです。それは "発生していない" だけのことなのです。

そんな、他の Web サイトからの腹立たしい Hot Link や、ダウンロードツールからの無遠慮なコンテンツの略奪行為をクールに回避するための秘密兵器が今回のサンプルコードになります、はい。(え、胡散臭いですか?)

 

IIS7 へのリクエストのハンドリング

IIS 7 の応答処理を拡張実現するオブジェクトとして、モジュールハンドラというものが用意されています。

この二つのオブジェクトの違いを簡単に説明すると、モジュールは、クライアントと Web サーバーの応答の間で処理を行うフィルタのようなものであり、ハンドラは、クリエストを受けてなにがしかの処理を実行するエンジンのようなものです。たとえば ASP.NET や、PHP 等を実行する FastCGI はハンドラです。

ふたつのオブジェクトの違いと詳細については以下のドキュメントを参照してください。

『HTTP ハンドラと HTTP モジュールの概要』
http://msdn.microsoft.com/ja-jp/library/bb398986.aspx

今回は、クライアントのリクエストが、コンテンツに到達する前に、リクエストの内容を調べ、リファラの内容が想定外であった場合はエラーページにリダイレクトする、という処理を行いますので、モジュールとして実装します。

"モジュールとして実装" と書くと難しく聞こえるかもしれませんが、System.Web.IHttpModule を継承したクラスを作成して、そのイベントハンドラ内で、引数として渡された URL を操作するだけなので、ASP.NET の経験者であれば簡単にプログラムを書くことが可能です。

具体的には、Visual Studio で、クラスライブラリでプロジェクトを作成し、コードを書いていきます。

ちなみにこれから紹介するサンプルコードについては、コンパイルする必要もありませんので、"Visual Studio を持っていない"、"プログラムを書けない" という方も、ぜひ設置してその効果をお試しください。

実際のサンプルコードと、IIS 7  への設置方法は以下の通りです。

  1. テキストエディタに以下のコードを貼り付けて、HotLinkBlocker_std.cs という名前で保存してください。

    using System;
    using System.Web;
    using System.Configuration;

    namespace MyIisExtentionModure
    {
        public class HotLinkBlocker_std : IHttpModule
        {
            public void Dispose() {}

            public void Init(HttpApplication context)
            {
                //リクエスト処理前のイベントハンドラを定義
                context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);
            }

            public void OnPreRequestHandlerExecute(Object source, EventArgs e)
            {
                HttpApplication app = (HttpApplication)source;
                HttpRequest request = app.Context.Request;
                HttpResponse response = app.Context.Response; 

                //リダイレクト用の URL を設定
                string redirectURL = ConfigurationSettings.AppSettings["RedirectURL"];

                //リファラを取得
                string requestReferer = request.Headers["Referer"];

                //リファラが null の場合はホットリンクと判断し、リダイレクト
                if (requestReferer == null) response.Redirect(redirectURL);

                //リファラの正誤判断用の URL の前半部分を設定
                string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];

                //リファラの内容が意図しないものの場合、リダイレクト
                if (isInvalidReferer(validReferer)) response.Redirect(redirectURL);
            }

            //リファラをチェック
            private bool isInvalidReferer(string requestReferer)
            {
                string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];
                return (validReferer != requestReferer.Substring(0, validReferer.Length));
            }
        }
    }

  2. Default Web Site 下の、ASP.NET が動作するように設定してある 仮想ディレクトリの物理フォルダ内に App_Code というフォルダを作成します。
  3. 作成した App_Code フォルダに HotLinkBlocker_std.cs ファイルを配置します。
  4. 同仮想ディレクトリ内の Web.config の <configuration> 内に以下の設定を追加します。

    <appSettings>
        <add key="ValidReferer" value="http://localhost/" />
        <add key="RedirectURL" value=http://localhost/iisstart.htm />
    </appSettings>
  5. IIS 管理ツールを起動します。
  6. 画面左のツリービューから目的の Web サイト、あるい仮想ディレクトリを選択します。
  7. [機能 ビュー] から [モジュール] アイコンをダブルクリックします。

  8. [モジュール] リストが表示されるので、画面右の [操作パネル] から [マネージモジュールの追加] リンクをクリックします。
  9. [マネージモジュールの追加] ダイアログボックスが表示されるので、[名前] テキストボックスに HotLinkBlocker と入力します。

  10. 同ダイアログボックスの [種類] ドロップダウンリストボックスから、"MyIisExtentionModure.HotLinkBlocker_std" を選択します。
  11. [OK] ボタンをクリックしてダイアログボックスを閉じます。
  12. Defauft Web Site の直下に、モジュールを設置した仮想ディレクトリ内のコンテンツへのリンクを記述した html ファイルを配置します。

以上で配置は完了です。

Default Web Site 直下の HTML 内のリンクから、Hot Link 防止用のモジュールを配置した仮想ディレクトリ内にあるコンテンツにアクセスし、問題なくブラウザに内容が表示されることを確認してください。

次に、先ほどブラウザに表示したコンテンツの URL を、新しく起動したブラウザのアドレスバーに入力し、リクエストが iisstart.htm にリダイレクトされることを確認してください。

 

モジュールの配置について

IIS 7 の拡張に使用される ASP.NET 2.0 では、実行時にコンパイルが行われるため、���ースコードをそのまま配置しても実行することができます。

その場合は、ASP.NET が実行可能となっている仮想ディレクトリのルートに App_Codeという名前でフォルダを作成し、そこにソースファイルを配置します。

もちろん、アセンブリとしてコンパイルした dll を使用することも可能です。

その場合は、ASP.NET が実行可能となっている仮想ディレクトリのルートに bin という名前でフォルダを作成し、そこにアセンブリファイル (dll) を配置します。

 

もう少し融通の利いた Hot Link 防止モジュール

前出の Hot Link 防止モジュールは、ソースを見やすくするためコード量を削っているので、動作的に融通が利きません。

たとえば、仮想ディレクトリ内すべてのコンテンツに Hot Link のチェックがかかるため、同ディレクトリ内のコンテンツから閲覧を開始することができません。(すべてリダイレクトされます)

以下に紹介するソースは、Web.config に定義した拡張子リストに合致するファイルのみ、Hot Link 防止機能が働きます。

ソースは以下のとおりです。

using System;
using System.Web;
using System.Configuration;


namespace MyIisExtentionModure
{
    public class HotLinkBlocker : IHttpModule
    {

        public void Dispose() { }

        public void Init(HttpApplication context)
        {
            //リクエスト処理前のイベントハンドラを定義
            context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);
        }


        public void OnPreRequestHandlerExecute(Object source, EventArgs e)
        {
            HttpApplication app = (HttpApplication)source;
            HttpRequest request = app.Context.Request;
            HttpResponse response = app.Context.Response; 

            //リクエストされたファイルの拡張子を取得
            string fileExtn = getFileExtension(request.FilePath);

            //拡張子がなければなにも処理しない
            if (fileExtn == "") return;

            //リクエストされた拡張子がチェック対象でなければ処理しない
            if (!isTargetExtension(fileExtn)) return;

            //リダイレクト用の URL を設定
            string redirectURL = ConfigurationSettings.AppSettings["RedirectURL"];

            //リファラを取得
            string requestReferer = request.Headers["Referer"];

            //リファラが null の場合はホットリンクと判断し、リダイレクト
            if (requestReferer == null) response.Redirect(redirectURL);

            //リファラの正誤判断用の URL の前半部分を設定
            string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];

            //リファラの内容が意図しないものの場合、リダイレクト
            if (isInvalidReferer(validReferer)) response.Redirect(redirectURL);
        }


        //リファラをチェック
        private bool isInvalidReferer(string requestReferer)
        {
            string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];
            return (validReferer != requestReferer.Substring(0, validReferer.Length));
        }


        //引数に指定された拡張子がチェックリストにあるか確認
        private bool isTargetExtension(string fileExtn)
        {
            string[] fileExtnArray;

            //カンマ区切りのチェック用拡張子リストを取得
            string targetFileType = ConfigurationSettings.AppSettings["TargetFileType"];

            //拡張子リストに "*" のみが指定されている場合はすべてのファイルをチェック
            if (targetFileType == "*") return true;

            //拡張子リストが空の場合は処理を行わない
            else if (targetFileType == "") return false;

            //拡張子リストを配列に確認
            fileExtnArray = targetFileType.Split(',');

            //リクエストされたファイルの拡張子がチェック対象であるか確認
            foreach (string targetExtn in fileExtnArray)
            {
                if (fileExtn == targetExtn) return true;
            }
            return false;
        }

        //リクエストされたファイルの拡張子を取得
        private string getFileExtension(string filePath)
        {
            string[] fileExtnArray = filePath.Split('.');
            if (fileExtnArray.Length >= 2)
            {
                return fileExtnArray[fileExtnArray.Length - 1];
            }
            else
            {
                return "";
            }
        }
    }
}

使用するには、テキストエディタに上記のソースコードを貼り付けて、HotLinkBlocker.cs という名前で保存して、仮想ディレクトリ内の App_Code フォルダにコピーしてください。

また、Web.config の <configuration> 内には、前出のサンプルより 1 行増えた、以下の設定を追加します。

<appSettings>
    <add key="ValidReferer" value="http://localhost/" />
    <add key="RedirectURL" value="http://localhost/iisstart.htm" />
    <add key="TargetFileType" value="jpg,jpeg,gif,png" />
</appSettings>

追加された TargetFileType には、Hot Link を防止したいファイルの拡張子を "," (カンマ) 区切りで指定します。

また、"*" (アスタリスク) を指定した場合には、すべてのファイルに Hot Link 防止機能が適用されます。""(空文字) を指定した場合は、Hot Link のチェックは行われません。

 

コンパイルしたい人向けの Visual Studio プロジェクト

今回紹介したモジュールの Visual Studio プロジェクトファイルを以下の SkyDrive にアップしましたので、アセンブリで配置したい人は使ってみてください。

アセンブリで使用したいけれど、Visual Studio を持っていない、という方は、以下から Visual Studio 2008 Express Editions (無償) を入手することができますので、こちらもお試しください。

『Visual Studio 2008 Express Editions』
http://www.microsoft.com/japan/msdn/vstudio/express/

Web Statistics

IIS7 の機能を拡張してみる-レスポンスヘッダー内のサーバー名の改ざん

$
0
0

Web サーバーが返す HTTP レスポンス内 のヘッダー Server には、Web サーバーの名前が含まれています。

以下は、実際に IIS 7.5 が返すレスポンスヘッダー中の Server の値です。

Server: Microsoft-IIS/7.5

この情報は、インターネットでどのような Web サーバーが使用されているかの調査等に利用されていますが、管理者の方からは、セキュリティの観点からこの情報を隠蔽、あるいは削除したいというご要望を受けることがあります。

残念ながら、IIS 7 の標準の機能では、この機能は提供されておりません。(※)

(※) IIS 管理ツールにある [HTTP 応答ヘッダー] の機能を使用してカスタムヘッダーの追加/削除などは可能です

しかし、先日の記事でご紹介したように、.NET を使用して IIS の機能を拡張することでその機能を実装することができます。

この処理に使用する コードの量は非常に少なくシンプルであり、コンパイルも不要かつ、配置も容易ということで、驚くほど簡単にその機能を実装することが可能です。

セキュリティを強化するため、使用している Web サーバーが Microsoft 社の Internet Information Server 7 であることを世間の目からひた隠しに覆い隠す、という行為は、セキュリティの向上という観点から理解できますが、しかし、これは、あくまでも私一個人の心情の問題にすぎないのですが、日々仕事で IIS にかかわる者としては少々複雑な気分にならざるをえません。

たとえばもし、お子さんから "同級生に見られたくないから授業参観に来ないで"とか、あるいは恋人から "人に見られなくないから離れて歩いて" とか言われたらどうでしょう?

この悲惨な心情たるや、あえてここで語るまでもないでしょう。

しかし、心ある人であればあるほど、無理をしてまで、その残酷な要求に笑顔で応えてしまうものなのです。

私が本日ここで、レスポンスヘッダーから IIS の名前を消すサンプルコードを紹介させていただくのも、もしかしたらそういう理由なのかもしれませんね。

......あ、あれ、変だな、涙が止まんないや。。

 

さて、冗談はこれくらいにして、

 

今回のサンプルコードの実装について紹介させていただきます。

レスポンスヘッダの改ざんは Web サーバーとクライアントの間で処理を行いますので、モジュールとして機能を実装していきます。

"モジュールとして実装" と書くと難しく聞こえるかもしれませんが、System.Web.IHttpModule を継承したクラスを作成して、そのイベントハンドラ内で、引数として渡された コンテキストを操作します。

前回記で紹介したのサンプルコードと非常に似ていますが、今回はレスポンスヘッダーを扱うということで、タイミング、つまりはイベントハンドラが異なりますので注意が必要です。実際のところ、私もここで結構悩みました。

今回使用するイベントハンドラは PreSendRequestHeaders です。

PreSendRequestHeaders イベントは ASP.NET が HTTP ヘッダーをクライアントに送信する直前に発生します。

ちなみに IHttpModule クラスのイベントと発生するタイミングについては、以下に技術情報が公開されていますので、興味のある方はぜひご覧ください。

『Visual C# .NET を使用して ASP.NET HTTP モジュールを作成する方法』
http://support.microsoft.com/kb/307996/ja

HTTP レスポンスヘッダー内の Server を書き換えるサンプルコードと配置方法は以下のとおりです。

なお、今回もコンパイルする必要はありませんので、"Visual Studio を持っていない"、"プログラムを書けない" という方も、ぜひ設置してその効果をお試しください。

  1. テキストエディタに以下のコードを貼り付けて、AlterHeaders.cs という名前で保存してください。

    using System;
    using System.Web;
    using System.Configuration;

    namespace MyIisExtentionModure
    {
        public class AlterHeaders : IHttpModule
        {
            public void Dispose() {}

            public void Init(HttpApplication context)
            {
                //ヘッダー送信前前のイベントハンドラを定義
                context.PreSendRequestHeaders += OnPreSendRequestHeaders;
            }

            void OnPreSendRequestHeaders(object sender, EventArgs e)
            {
                //<appSettings> から情報を取り出す
                string headerValue = ConfigurationSettings.AppSettings["ALTER-HEADER_Server"];

                //ヘッダー "Server" の内容を書き換え
                HttpContext.Current.Response.Headers.Set("Server", headerValue);
            }
        }
    }

  2. Default Web Site 下の、ASP.NET が動作するように設定してある 仮想ディレクトリの物理フォルダ内に App_Code というフォルダを作成します。
  3. 作成した App_Code フォルダに AlterHeaders.cs ファイルを配置します。
  4. 同仮想ディレクトリ内の Web.config の <configuration> 内に以下の設定を追加します。

    <add key="ALTER-HEADER_Server" value="MySweetWebServer" />
  5. IIS 管理ツールを起動します。
  6. 画面左のツリービューから目的の Web サイト、あるい仮想ディレクトリを選択します。
  7. [機能 ビュー] から [モジュール] アイコンをダブルクリックします。

  8. [モジュール] リストが表示されるので、画面右の [操作パネル] から [マネージモジュールの追加] リンクをクリックします。
  9. [マネージモジュールの追加] ダイアログボックスが表示されるので、[名前] テキストボックスに AlterHeaders と入力します。

  10. 同ダイアログボックスの [種類] ドロップダウンリストボックスから、"MyIisExtentionModure.AlterHeaders" を選択します。
  11. [OK] ボタンをクリックしてダイアログボックスを閉じます。

以上で配置は完了です。

NetmonFiddlerWiresharkなどを使用してネットワークをキャプチャして、HTTP レスポンス中のヘッダー Server の内容が変更されていることを確認してください。

以下に私の環境での実行結果を載せておきます。

Z(ゼータ)IIS。「これが若さというものか」的な。

Victory(ビクトリー)IIS。「おかしいですよ、カテシナさん!」的な。

Windows OO (ダブルオー)。「おれが、Windows だ!!」的な。

Visual Studio のプロジェクトを SkyDrive にアップしましたので、コンパイルして使用したい方はこちらをご査収ください。(前回紹介した HotLinkBlocker も含んでいます)

Web Statistics

IIS7 の機能を拡張してみる-指定した言葉の出力禁止

$
0
0

IIS7 は ASP.NET とのパイプライン (リクエストを受けてからレスポンスを返すまでの処理) の統合により、パイプラインにかかわる処理を .NET のマネージコードで記述することができます。

リクエストのハンドリング方法については、既に機能を紹介していますので、今回はレスポンスはハンドリングし、その中のコンテンツを書き換える方法について紹介させていただきます。

この "コンテンツの内容を書き換える = 出力を行う" という処理は本来ハンドラで行う処理ですが、今回はモジュールを使用したものを紹介します。

そのため紹介さていただきますサンプルコードは、既にハンドラが設定されているページ、つまりは、*.aspx や *.php 等の、サーバーサイドで何らかの処理を行うページでしか動作しません。

それなのに、なぜあえてモジュールとして実装したかというと、ええ、....じつは私の勘違いでモジュールとして作ってしまったからです、はい(恥)

以前、php アプリケーション内のリンクを、フレンドリな短い URL に書き換えるというモジュールを作成したこともあり、"ASP.NET 以外のページでも動作 = 静的なファイルでも動作" と短絡的に考えていたのですが、今回モジュールを作り終えてテストしたところ、静的なファイルでは動作しないこと&理由が判明したしだいです。(すみません)

 

今回のサンプルの機能

のっけから非常に残念な感じの今回のサンプルなのですが、どういった機能を実装したかというと、レスポンス内のコンテンツから任意の言葉を伏字にするという機能です。

この機能を使用すると、任意に指定したワード (言葉) がコンテンツに含まれていた場合、代替えのキャラクタで置き換えてくれるため、サイトの訪問者の目に触れさせたくない言葉をブロックすることができます。

人によっては "そんな機能が本当に必要か? 必要なのか??" と考えられるかもしれません。

しかしどうでしょう、 たとえば、取引先の社の Web サイトを覗いたときに、そのコンテンツ内に公序良俗に反する言葉や、差別用語や、社会通念上許されざる言葉が平然と使用されていたとしたら、今後その会社と深いリレーションを築きたいと思うでしょうか?

また、いくら仲の良い友人からのメールであっても、幼児の三大爆笑ワードである "ば〇、う〇こ、ちん〇ん" といったような、下品な言葉が毎回多用されていたりしたら、その人の品性と精神的健康状態について強い疑問を持たざるを得ないでしょう。

さらに"言葉" には、理解した瞬間に著しく相手の気分を害するものもあります。しかもそれは、人間の肉体に備わった機能ではブロックすることはできません。

たとえば、今、このドキュメントをお読みいただいているわけですが、ご自身の肉眼に実際に写っているものは、じつは "文字" でも "言葉" でなく、モニタ上に並んだ映像素子の点滅でしかありません。

モニタに表示された光のパターンを脳が文字と認識し、文字の並びから文としての解釈が行われ、私の言葉が"再生" (処理が実行) されているのです。

この一連の処理は、人間の読解における基本的な処理ですが、基本的であるがゆえにその一連の処理は無条件に実行され、解釈される内容の良し悪しにかかわらず、その実行は防ぎようがないのです。

たとえば、ここに突然、閲覧者にとって非常に有害な言葉が書かれていたとしてもそれを防ぐ手立ては、閲覧者自身にはないのです。

そういった意味では人間は情報に対して非常に無防備な状態であると言えるでしょう。

そんな、情報に対し "まるごし" でやってくる閲覧者を保護し、迂闊なコンテンツによる自サイトのイメージ低下を防いでくれるのが今回のモジュールなのです。(すみません、ほんというと今考えました)

 

IIS 7 のクライアントへのレスポンスのハンドリング

今回のサンプルは IHttpModule を継承してモジュールとして実装していますが、そのため、HTML などの静的なページでは動作せず、ASP.NET や、PHP といったハンドラを使用しているページでのみ動作します。

リクエストを処理してクライアントへ何らかのレスポンスを返す IIS 拡張を作成する場合はハンドラで作成するようにしましょう。(はい、すみません)

サンプルコードの配置方法は以下の通りです。今回もコンパイルは任意ですので、"Visual Studio" を持っていない、"プログラムを書いたことがない" という方もぜひお試しください。

  1. テキストエディタに以下のコードを貼り付けて、WordFilter.cs という名前で保存してください。

    using System;
    using System.IO;
    using System.Text;
    using System.Web;
    using System.Text.RegularExpressions;
    using System.Configuration;
    using System.Web.Caching;


    namespace MyIisExtentionModure
    {
        public class WordFilter : IHttpModule
        {
            public void Dispose() {}

            public void Init(HttpApplication context)
            {
                //イベントハンドラの登録
                context.PreRequestHandlerExecute += new EventHandler(application_PreRequestHandlerExecute);
            }

            //イベント ハンドラ (ページ、XML Web サービスなど) の実行を開始する直前に発生
            void application_PreRequestHandlerExecute(object sender, EventArgs e)
            {
                HttpApplication app = (HttpApplication)sender;
                HttpContext context = app.Context;
                context.Response.Filter = new ContentsRewriter(context);
            }
        }

        //レスポンスコンテキスト中のレスポンスを書き換える
        public class ContentsRewriter : Stream
        {
            private HttpContext context;
            private Stream innerStream;
            private MemoryStream bufferStream;

            //コンテキストをリライト
            public ContentsRewriter(HttpContext contxt)
            {
                context = contxt;
                innerStream = context.Response.Filter;
                bufferStream = new MemoryStream();
            }

            public override void Write(byte[] buffer, int offset, int count)
            {
                //レスポンスをバッファに保存
                bufferStream.Write(buffer, offset, count);
            }

            //レスポンスをフラッシュ
            public override void Flush()
            {
                if (bufferStream.Length == 0) return;

                Encoding enc = Encoding.GetEncoding(context.Response.Charset);
                byte[] responseBytes = bufferStream.ToArray();
                bufferStream = new MemoryStream();

                //レスポンスされるコンテンツを文字列として取り出す。
                //単純にレスポンスされるコンテンツの内容を編集したい場合は、
                //変数 responseString の内容を文字列操作。
                String responseString = enc.GetString(responseBytes);

                //伏字処理。
                responseString = execWordBlock(responseString);
                responseBytes = enc.GetBytes(responseString);
                innerStream.Write(responseBytes, 0, responseBytes.Length);
                innerStream.Flush();
            }

            //伏字処理を実行
            private string execWordBlock(string responseString)
            {
                string subChar = ConfigurationSettings.AppSettings["SubstituteChar"];
                string[] blockWordList;

                //使用している正規表現文字列が、改行があるとうまく動作しないため改行を削除
                string contectString = responseString.Replace(Environment.NewLine, "");

                //キャッシュに禁止単語のリストがあるかチェック
                if (HttpRuntime.Cache["BlockWordList"] == null)
                {
                    //キャッシュに無い場合はファイルから情報を読み込みリストを作成
                    string blockWordFilePath = ConfigurationSettings.AppSettings["BlockWordListPath"];
                    string listString = File.ReadAllText(blockWordFilePath, Encoding.Default);
                    blockWordList = listString.Split(',');
                    //リストをキャッシュに保存
                    HttpRuntime.Cache.Insert("BlockWordList", blockWordList);
                }
                else
                {
                    //キャッシュからリストを取り出し
                    blockWordList = (string[])HttpRuntime.Cache["BlockWordList"];
                }

                foreach (string chkWord in blockWordList)
                {
                    string subWord = MakeSubstituteString(chkWord.Length - 1, subChar);

                    //正規表現を使用した置換処理を行う関数を呼んでいますが、
                    //ここで単純に Replace してしまったほうがパフォーマンスは良いでしょう。
                    contectString = FilterWord(contectString, chkWord, subWord);
                }
                return contectString;
            }

            //検出されたワードを伏字で置き換える
            private string FilterWord(String inputString, string chkWord, string subWord)
            {
                //正規表現の文字列。より適切なものがあれば変えてください。
                string regExpString = ">.*?<";
                string inputString_wrk = inputString;
                Regex regExp = new Regex(regExpString,
                    RegexOptions.IgnoreCase | RegexOptions.Compiled);

                for (Match mtc = regExp.Match(inputString); mtc.Success; mtc = mtc.NextMatch())
                {
                    string targetString = mtc.Groups[0].ToString();
                    string cureString = targetString.Replace(chkWord, subWord);
                    inputString_wrk = inputString_wrk.Replace(targetString, cureString);
                }
                return inputString_wrk;
            }

            //伏字に使用する文字列を生成する
            private string MakeSubstituteString(int wordLength, string subChar)
            {
                string returnString = "";
                for (int i = 0; i <= wordLength; i++)
                {
                    returnString += subChar;
                }
                return returnString;
            }

            public override void Close()
            {
                try { Flush(); }
                catch (Exception) { throw; }
                finally { }
            }

            /* 以下 Stream クラスを継承するための抽象メンバ */
            public override bool CanRead
            {
                get { return false; }
            }

            public override bool CanSeek
            {
                get { return false; }
            }

            public override bool CanWrite
            {
                get { return true; }
            }

            public override long Length
            {
                get { throw new NotImplementedException(); }
            }

            public override long Position
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            public override void SetLength(long value)
            {
                throw new NotImplementedException();
            }

            public override long Seek(long offset, SeekOrigin origin)
            {
                throw new NotImplementedException();
            }

            public override int Read(byte[] buffer, int offset, int count)
            {
                throw new NotImplementedException();
            }
        }
    }

  2. Default Web Site 下の、ASP.NET が動作するように設定してある 仮想ディレクトリの物理フォルダ内に App_Code というフォルダを作成します。
  3. 作成した App_Code フォルダに WordFilter.cs ファイルを配置します。
  4. テキストエディタに表示禁止文字を , (カンマ) 区切りで入力し、BlockWordList.dic という名前で保存します。(UTF-8 で保存してください)
  5. 作成したファイル BlockWordList.dic を仮想ディレクトリのルートに配置します。
  6. 同仮想ディレクトリ内の Web.config の <configuration> 内に以下の設定を追加します。

    <add key="BlockWordListPath" value="BlockWordList.dic ファイルまでの物理パス" />
    <add key="SubstituteChar" value="×などの伏字に使用するキャラクタ" />
  7. IIS 管理ツールを起動します。
  8. 画面左のツリービューから目的の Web サイト、あるい仮想ディレクトリを選択します。
  9. [機能 ビュー] から [モジュール] アイコンをダブルクリックします。

  10. [モジュール] リストが表示されるので、画面右の [操作パネル] から [マネージモジュールの追加] リンクをクリックします。
  11. [マネージモジュールの追加] ダイアログボックスが表示されるので、[名前] テキストボックスに WordFilter と入力します。
  12. 同ダイアログボックスの [種類] ドロップダウンリストボックスから、"MyIisExtentionModure.WordFilter" を選択します。
  13. [OK] ボタンをクリックしてダイアログボックスを閉じます。

表示禁止に指定した言葉を含んだ *.aspx ファイルを仮想ディレクトリに配置し、ブラウザからアクセスして、伏字が表示されることを確認してください。

私の環境での実行結果を掲載します。"PHP" という言葉を "♥"(黒はーと) で伏字にしています。


"♥" を効果的に使い、コンテンツのセクシーさ神秘性を上げた、おしゃれ上級生のテク。

 

このサンプルコードの反省点

今回のサンプルコードの至らなかった点を以下にまとめましたので、このサンプルからインスパイアされてなんか作るさいは以下の点にご注意ください。

  1. モジュールで作成してしまつたために静的ファイルを処理しない

    処理を行い何らかのコンテキストを返す場合はハンドラで作りましょう。

  2. 正規表現の条件文字列がよくない

    正規表現の検索に使用する文字列を ">.*?<" としていますが、この指定はすべての >< 間の文字列を取得する条件であり、効率が良くありません。実際のところ処理速度は速いと言えません。"禁止ワードを含んだ >< 間" という条件を設定することで処理速���のアップが見込めます。また、日本語などの HTML のタグやファイル名に使用される可能性が低い文字列のみ検閲する場合は、正規表現を使用せずに単純に Replace 処理をしたほうが実行速度が上がります。

  3. キャッシュのクリア機能を実装していない

    今回のサンプルプログラムでは、パフォーマンス低下を防ぐため、表示禁止ワードのリストをインメモリキャッシュに保持します。このキャッシュをクリアするには、アプリケーションプールをリサイクルするか、仮想ディレクトリ内の web.config, bin フォルダ内のファイル、App_Config フォルダ内のファイルいずれかを更新します。
    なお、リストファイルの更新と合わせてキャッシュをクリアする機能を実行するには、以下のキャッシュ追加の Insert メソッドの第 3 引数にリストファイルを指定して作成した CacheDependency オブジェクトを指定します。(ただし実行して試したわけではありません)

    //リストをキャッシュに保存
    HttpRuntime.Cache.Insert("BlockWordList", blockWordList);

今回のサンプルコードはイマイチですが、念のために SkyDrive にプロジェクトをアップしましたので、コンパイルして使いたい方はどうぞ。(App_Data フォルダの代わりに Bin フォルダを作成して、dll を配置します。IIS 管理ツールでの操作は同じです。)

http://cid-a52c0f4368cad145.skydrive.live.com/self.aspx/MSDN%5E_Blog/WordFilter.zip

 

お知らせ
==========

IIS の師匠 奥主 さんと一緒に作成している"インターネット Web サーバー構築ガイドライン (ドラフト版)"が公開されました。

http://technet.microsoft.com/ja-jp/iis/ff625168.aspx

"IIS を使いたいけれど、なにから初めていいかわからない" というお客様の声から生まれました。

引き続きこれからもリリース予定ですので、周りに Web サーバーを使ってみたいという方がいらっしゃいましたらぜひご紹介のほどよろしくお願いいたします。

Web Statistics

IIS 7 版 mod_info、mod_status 的なもの

$
0
0

最近、イベントの登壇やらドキュメントの作成やらで、とんとコードを書く機会が無かったのですが、久々にひとつ書くことができました。

今回作成したのは Apache における mod_info や mod_status のようなものです。

作成に至った経緯はほかでもありません。

現在、IIS の師匠、奥主さんと二人で 『インターネット Web サーバー構築ガイドライン (ドラフト版) 』 というのを書いているのですが、その作業中に、奥主さんより、“Apache には動作状況を Web ブラウザから確認できる mod_info や mod_status というのがあるんだけと、IIS にはないみたいだから作って” と依頼されたためです。

そう、IIS7 では “ないものは作ってしまえ” ということが可能なのです。

というわけで、作成することになったのですが、いまさら Apache と同じものを作ってもあんまりクールじゃないなぁってことで、”IIS  における、IIS ユーザーのための、IIS による状態報告ツール” といういうことで IIS の構造に合わせて作成してみました。

また、情報の出力形式についても、人間だけでなく、アプリケーションからも使用できるようにしようということで、コンテンツの出力正式に複数のフォーマットをサポートするようにしました。

具体的には以下のような機能を持っています。

 

各 IIS オブジェクトのリソース状況のブラウザ表示

IIS 7 の構造に合わせ、”サーバー本体”、”Web サイト”、”アプリケーションプール”、”ワーカープロセス” に分かれて現在の状態が表示されます。またページ内のリンクをクリックすることで、目的のオブジェクトのさらに詳細な情報にアクセスすることができるようになっています。

image
図 : IIS リソース情報画面

たとえば、ワーカープロセスのページでは、現在処理を行っているリクエストの簡単な内容なども確認することができます。

image
図 : プロセス内で処理中のリクエストの情報を取り出したところ

 

XML、SOAP、JSON でのデータ出力

今回のサンプル ハンドラーが出力する情報は、ブラウザーに Web ページとして表示されますが、実は IIS からレスポンスされているコンテンツは HTML ではなく XML です。これに XSL を適用し、人間にも分かり易いように成形しています。これは、ブラウザーで HTML ソースを表示することで確認できます。

そのためアプリケーションなどからはこのデータを直接 Rest データとして利用することができます。

ちなみにクエリーストリングを style=xmlと指定すると、スタイルシート (XSL) が外れた状態でデータが返るので、ブラウザで XML を確認することができます。

image
図 : XSL の参照を含まない XML での出力 (style=xml)

また、クエリーストリング styleの指定内容を変更することで、XML、SOAP、JSONのフォーマットでデータを返すことができます。

これらを利用して、定期的にリクエストを投げてサーバーの監視を行うプログラムを作成するも良いかもしれませんね。(え、”SOAP を使いたいけど WSDL はどこだ?” って。えっと、それはですね、えーと、…そのうち書きます。。。 )

image
図 : SOAP 形式での出力 (style=soap)

 image
図 : JSON 形式での出力 (style=json)

以下にセットアップ方法と使い方を示しますので、興味をそそられた方はぜひお試しください。

 

サンプル ハンドラーのセットアップ

準備

  1. 以下のリンクより zip ファイルをダウンロードし、内容を取り出してください。
  2. IIS 上に仮想ディレクトリを “iisresview” という名前で作成し、仮想ディレクトリの参照元フォルダに  zip から取り出した以下のファイルを配置します。

    iisresview_srv.xsl
    iisresview_srv.xsl
    iisresview_site.xsl
    iisresview_pool.xsl
    iisresview_wp.xsl

  3. アプリケーションプールを “iisresviewPool” という名前で新規に作成し、LocalSystemアカウントで動作するように構成します。このアカウントがもつ強力な権限は、サーバーの各情報を取得するために必要です。
  4. 仮想ディレクトリ “iisresview” を ASP.NET が動作するよう、”アプリケーション” に変換 ( ※1) します。 このとき、アプリケーションプール “iisresviewPool” を使用するように構成します。

以上で準備は完了です。

(※1) 作業内容が不明な場合は 『インターネット Web サーバー構築ガイドライン (ドラフト版)』の「第 6 章 アプリケーションを実行環境を設定しよう」 をご参照ください。
(※2) “bin” フォルダが存在しない場合は、エクスプローラーで作成してかまいません。

 

“bin” フォルダに配置したファイル MyResourceWatch.dll  はハンドラーとして作成してありますので、これを動作するように登録を行います。

手順は以下のとおりです。

 

ハンドラーの構成手順

  1. IIS マネージャーを起動します。
  2. IIS マネージャーの画面左のツリービューを展開し、[iisresview] を選択します。
  3. 画面中央の [機能 ビュー] よりで [ハンドラー マッピング] のアイコンをダブルクリックします。

    image

  4. 画面右の [操作] ウィンドウより [マネージ ハンドラーの追加] リンクをクリックします

    image

  5. [マネージ ハンドラーの追加] ダイアログボックスが表示されるので、各項目を以下のように設定し [OK] ボタンをクリックします。
    要求パスiis.info
    種類MyResourceWatch.XmlView
    名前IISResourceView

    image 


以上で設定は完了です。

Web ブラウザーを起動して以下の URL にアクセスし、IIS 7 のリソース使用状況を示す画面が表示されることを確認してください。

http://localhost/iisresview/iis.info

 

セキュリティについて

前出のセットアップ手順にて、アプリケーションプールに Local Systemのアカウントを使用しているように、このサンプル ハンドラーを実行させるには、サーバーのローカル リソースに対する強力な権限が必要なります。

そのため運用を誤れば重大なセキュリティリスクを負いかねませんので、もし第三者からのアクセスが想定されるインターネットなどの環境で、本サンプルを検証される際には、認証機能によるアクセス制御や、SSL 等を使用して十分なセキュリティの安全を���保していたたけますようお願いいたします。

 

ソースコードについて

いつもであればここにソースコードを載せるのですが、今回はコード量が多いので割愛させていただきます。前出の zip ファイルの中にサンプルプロジェクトが同梱されておりますので、お手数ですがそちらでご確認ください。

なお、サンプルプロジェクトは Visual Studio 2010 (日本語版) で作成を行いました。

もし、Visual Studio 2010 をお持ちでない方は以下の URL から Visual C# 2010 Express (無償)を入手して使用することができます。

Microsoft Visual Studio Express
http://www.microsoft.com/japan/msdn/vstudio/express/

また、なんらかの理由で、Visual Studio 2008 しか使用できない場合は、.NET Framework 3.5 を使用する空のプロジェクトを作成し、サンプル プロジェクトから *.cs ファイルをインポートしてください。さらに以下のアセンブリに参照設定を行ってください。

System.ServiceModel.Web.dll
System.Xml.dll
System.Web.dll
System.Management.dll
Microsoft.Web.Management.dll
Microsoft.Web.Administration.dll
System.Runtime.Serialization.dll
System.Runtime.Serialization.Formatters.Soap.dll

 

さて、そろそろ 7 月 10 日 (土) に登壇予定の オープンソースカンファレンス 2010 Kansa@Kyotoのセッションの準備をしないと。。

ではまた。

Web Statistics

IIS7.x での共有 Web ホスティングのためのサンプルアプリケーションVer2

$
0
0

以前このブログでも紹介させていただいたのですが Windows Server 2008 に搭載されている IIS 7.x を使用すると、IIS Manager for Remote Administration ( IIS 管理ツール)を使用して簡単な Web ホスティングサービスをすぐに開始することができます

分かり易くいうと、自分のサーバーの IIS 上に作成した Web サイトを貸し出すことができる ( 正しくは、”管理させることができる” ) のです。

“貸し出された” 側、つまり ユーザーは、自分のマシンの IIS 管理ツールを使用して、インターネット越しに自分に割り当てられた Web サイトを管理/運用することができます。 (※ポートの設定が必要ですが)

もちろん ユーザーは FTP を使用してコンテンツのアップロードが可能ですし、ASP.NET、PHP アプリケーションの配置/実行も行えます。また自分の Web サイト独自のユーザーを作成して、ディレクトリにアクセス権を設定したりということも可能です。

非常に便利で強力な機能なのですが、貸し出すサイトが多いと、Web サイトの作成や、共有に関わる設定などなかなか手間がかかります。

そこで以前、これらの処理を自動で行うサンプルアプリケーションを公開したのですが、今回はそのバージョンアップ版です。

以下は、このアプリケーションが行う処理の順番と機能を示しており、新たに追加された機能は青字の部分になります。

  1. インターネット インフォメーション サービスマネージャーユーザー(※以下、"IIS 管理ユーザー") を作成
  2. Web サイト用の物理フォルダを作成
  3. Web サイト用の物理フォルダにディスククォータ ( サイズ制限 ) を設定 (※1)
  4. Web サイト用の物理フォルダにファイルスクリーン ( 配置制限 ) を設定 (※2)
  5. アプリケーションプールの作成
  6. Web サイトの作成
  7. 作成した Web サイトに FTP をバインド
  8. 作成した Web サイトに、作成した IIS 管理ユーザーを管理者として登録
  9. PHP ランタイムの設定
  10. IIS 管理ユーザー専用のデータベースを作成 (※3)

※1 ディスククォータについては、過去記事 『コードによるディスククォータの設定』 の内容をご参照ください。
※2 ファイルスクリーンについては、過去記事 『コードによるファイルスクリーン機能の実装』 の内容をご参照ください。
※3 IIS 拡張である IIS Database Managerを使用すると、IIS 管理ツールからサーバー上のデータベースの管理が可能です。もちろんインターネット経由でもです。詳しくは過去記事 『Web サイト共有サービスでの DB 機能提供の実装』 の後半部分をご参照ください。

アプリケーションの画面は以下の通りです。

image
( 図: サンプル アプリケーションの UI )

前回のサンプルアプリケーションは Web の UI と WCF も提供させていただいていましたが、”コードのサンプル” であれば Windows フォームのサンプルで充分と思われますので、今回は Windows フォームアプリケーションのみとなります。

…まぁ、ほんとうは次の仕事が詰まってて時間が取れなかったからなのですが (^^;)

このサンプル アプリケーションが動作する条件は以下の通りです。

OS : Windows Server 2008 および 同 R2

必須インストール :
IIS 7.x ( 要 HTTP,FTP, ASP.NET )
 ファイル サーバー リソース マネージャー
SQL Server 2008 および 同 R2 ( Express Editionでも可)

 

サンプルアプリケーションのプロジェクトファイルを以下の SkyDrive にアップしましたので、IIS の構成/操作を .NET コードで行ってみたいという方は、ぜひダウンロードしてお試しください。

なお、詳しい配置方法、操作方法につきましては、Zip ファイル内に Readme.txt を用意しましたのでそちらをご覧ください。 (長くてうんざりすると思いますが)

 

さて、このコード、実はずいぶん前から公開したかったのですが、いろいろと別件が重なってこの時期となってしましました。

ようやく公開できて良かったです。

 

Web Statistics

“IIS 7 版 mod_info 的なもの” を jQuery Templatesで使う

$
0
0

先週、マイクロソフトが .NET jQuery Extensionsとして開発を進めてきた機能が公式な jQueryのプラグインとして公開されました。

これについて詳しい説明は、同僚の井上( 章 )  のブログで見ていただくとして、このプラグインの中に jQuery Templateというものがあります。

これは JavaScript オブジェクトに、HTML ドキュメントの <script> タグ内であらかじめ HTML で定義しておいた UI ( テンプレート ) を適用して描画してくれるという、いわゆるクライアントレンダリングの機能です。

これについての詳しい説明も、同僚の井上( 章 )  のブログで見ていただくとして (オイ)、このブログでは、以前の記事で紹介した “IIS 7 版 mod_info 的なものをサービスとして使用するツール” から JSON でデータを取り出し、描画するサンプルを紹介します。

image

と、言っても、実際の HTML を記述すると長くなってし��うので、以下の SkyDrive からダウンロードしていただくことになるのですが。

じつは、 この記事で紹介している XSL を使用したページの描画は、InternetExplorer ではうまく行えませんでした。

しかし、今回の jQuery Templates を使用したページは、他のブラウザでも正しく描画されます。( いまさらなんですが汗 )

 

サンプルの配置について

今回のサンプル HTML は、この記事で紹介した IISResourceWatchが返す JSON と jQuery Template を使用して 1 つのファイルで 4 つの画面をまかないます。

また、使用しているスクリプトのライブラリも、マイクロソフトの CDN 上のものを使用しているので、追加のファイルも必要ありません。

前出の記事の内容をもとに IISResourceWatch ハンドラ (※) を IIS に設定し、その仮想ディレクトリ内にダウンロードした iisinfo.htm ファイルを配置すれば完了です。
(※) 今回のサンプルの検証中に、以前公開した MyResourceWatch が Web サイトの情報取得時に JSON が返さない不具合を発見したため修正し SkyDrive 上のファイルをアップデートしました。その際にネームスペースとアセンブリ名を MyResourceWatchから IISResourceWatchに変更しましたので、すみませんが適宜読み替えて作業ください。ちなみに、原因は if 文の簡単な書き間違いでした…Orz

今回のサンプルで行っているように、jQuery Template を使用すると、サーバーでホストされた JSON に対しクライアント側で直接 UI を割り付けることができます。

この組み合わせでアプリケーションをいくつか作成してみると気が付くのですが、アプリケーションの UI 生成はほとんどの場合、この組み合わせで充分で、わざわざサーバーサイドで UI を生成する必要がない場合がほとんどです。

つまりサーバーサイドではアプリケーションを純粋なサービスとして作成し、クライアント側では好きな UI を割り付けて使用することができるのです。とうぜん、UI を変更するためだけにアプリケーションのファイルを変更する必要はありません。

さらに、今回 jQuery Template とともに追加された機能のひとつである jQuery Datalinking を使用すると、JavaScript オブジェトと HTML のコントロールを連結することができますので、HTML で記述した UI  と JavaScript オブジェクトの間の更新処理をいちいちコードで記述する必要もなくなります。

そして、更新された JavaScript オブジェクトを JSON としてサーバーにポスト し(※)、サーバ上のアプリケーション側では JSON をデシリアライズすればオブジェクトとして扱うことができます。
( ※ ) ここで、”ふつーに、form のポストでダメなのか?” という声もあるかと思うのですが、そうするとアプリケーションのロジックがブラウザ側の UI  の構造に縛られてしまうのであまりウツクシクないと思います。

そうです。JSON と jQuery Template を組み合わせることで、ビジネス ロジック部分と UI 部分を完全に分離させることができるのです。

とくに ASP.NET MVC Framework には JSON メソッドというのが用意されており、MVC の Controller のオブジェクトを簡単に JSON としてレスポンスすることができますので、これらを積極的に組み合わせてアプリケーションを作成するのも面白いと思います。

ところで “MVC から View ( UI ) が無くなってしまったら、MVC とは言わないのでは?” と考える方もいると思うのですが、結局のところこの方法は View の部分を外出しにして静的なファイルで描画する、ということにすぎませんので、MVC の考え方になんら違いはありません。

ここらへんについてはデモを交えて Tech・Ed 2010 の公開セッションで話しておりますのでよろしければご覧ください。

T6-306 jQuery と ASP.NET AJAX Control Toolkit による AJAX アプリケーション開発
http://msdn.microsoft.com/ja-jp/events/ff973814.aspx

jQuery のような便利なライブラリの機能追加や、HTML5 の登場など、Web はますます面白くなってきそうですね。

Web Statistics

SQL Server のサンプル DB AdventureWorks 2008R2 SR1にデータベース AdventureWorks2008R2 が入っていない件

$
0
0

現在、PHP コードから SQL Server のレポート機能を使用するという記事を書いています。

CodePlex に SQL Server Reporting Services (SSRS) SDK for PHP なるものが用意されていてこれを使用します。

SQL Server Reporting Services (SSRS) SDK for PHP
http://ssrsphp.codeplex.com/

さて、本日、パッケージの中に用意されているチュートリアルを使用してサンプルを作って動作確認しようと思ったのですが、内部のクエリーが使用している “AdventureWorks2008R2 というデータベースがみつからない” 旨のエラーが出て動作しませんでした。

サンプルデータベースは、CodePlex の Microsoft SQL Server Community Projects & Samples から落としてきた最新の AdventureWorks 2008R2 SR1 を使用しており、同ページのインストールされるデータベースの一覧にも “AdventureWorks2008R2” が記載されているので問題はないはずです。

Microsoft SQL Server Community Projects & Samples
http://msftdbprodsamples.codeplex.com/

しかし、SQL Server Management Studio を使用して SQL Server 内のデーターベースを確認すると、なるほど AdventureWorks2008R2 という名前のデータベースはありません。

代わりに (?)  AdventureWorks という名前のデータベースがあるので、きっと名前が違っているのであろう、と接続文字内の DB 名を AdventureWorks に変更してみたものの、やはり動きません。。。

これは、SSRS のサンプルが間違っているのかも、と思い、以下のチュートリアルも試してみたのですが、ことごとく動作しません。

チュートリアル: AdventureWorks 2008R2 サンプル レポートの作成 (SSRS)
http://technet.microsoft.com/ja-jp/library/ff487370.aspx

それから数時間、迫る締切に脂汗を流しながら Web の情報を探しまくった結果、フォーラムの投稿から AdventureWorks 2008R2 SR1 には含まれていないことがはんめえ…..。

Why SSMS2008 show AdventureWorks database only? No AdventureWorks2008R2?
http://social.msdn.microsoft.com/Forums/en-US/sqlserversamples/thread/7abe15df-fc30-4e99-b0df-904b1b9e5a05

私の調査に費やした数時間って、いったい。。。

こみあげてくる行き場のない怒りを抑えるために、なにか楽しかったことを考えようと思ったのですが、最近楽しかったことなど、とくになにもないことに思い至り、よけいにやるせない気持ちになってしまったしだいです。

さて、AdventureWorks を使用する人々が、私のようなこんなやるせない気持ちにならないように 回避策を紹介させていただきます。

 

AdventureWorks 2008R2 データベースが見つからない場合の回避策

2011 年 2 月 7 日現在、AdventureWorks 2008R2 SR1 には、AdventureWorks 2008R2 データベースは含まれていません。

回避するためには AdventureWorks 2008R2 データベースのデータベースファイルを入手して、データベースをコマンドで作成する必要があります。

具体的には、以下のページから AdventureWorks 2008R2 の mdf ファイルをダウンロードして入手します。

AdventureWorks2008R2 without filestream
http://msftdbprodsamples.codeplex.com/releases/view/59211

そして、以下のコマンドを使用してデータベースを作成する必要があります。

CREATE DATABASE AdventureWorks2008R2
      ON (FILENAME = '<drive>:\<file path>\AdventureWorks2008R2_Data.mdf')  -- change the drive and file path
      FOR ATTACH_REBUILD_LOG ;

これから SQL Server のレポート機能を試したり、AdventureWorks データベースを使用する、という方が身近にいたらぜひこの内容をお伝えくだい。

こんなやるせない思いをするのは私ひとりでたくさんですので。

あー、なんかすごい悪口言いたい….。

 

Web Statistics

Web フォントとハートブレイク・カフェ

$
0
0

前回のブログ記事でお伝えしたとおり、セッションで使用するデモ用に、HTML5 + CSS3 を使った架空の喫茶店のページを作成しております。

その喫茶店の名は「ハートブレイク・カフェ」。

店のコンセプトは、「傷ついた街の天使が羽根を休める….、ということはどうでも良くて、前回の記事から、このサンプルに使用している ”いまどきの” Web 技術について紹介しております。

今日は Web フォントについて書きたいと思います。

「ハートブレイク・カフェ」のロゴは、画像ではなく  Web フォントの 「Lobster」 というフォントを指定して表示しています。

image

ちなみにこの「Lobster」というフォントは Windows には標準で含まれていませんが、Web フォントをサポートしている Web ブラウザーでアクセスすると、Windows はもちろん、Mac でも Linux でも「Lobster」 フォントが正しく表示されます。(※ コーヒーカップの絵は画像ですよ。)

 

Web フォントとは

Web フォントは、クライアントにインストールされていないフォントであっても、Web からフォント提供することで、指定通りの字体の表示を行わせる技術で、CSS3 でサポートされています。(※)

フォントというと、ほとんどの場合機種依存であり、異なるプラットフォームで同じ文字を表示をさせるには、形状が似たフォントを指定するなどの工夫が必要でしたが、 Web フォントを使用すると、こういった面倒はありません。

(※) 実は、Web フォントは CSS3 以前から存在しており、以前より CSS の @font-face から使用することができました。機能自体の実装もけっこう早くて、Internet Explorer では 4 の時代からサポートしていました。Web フォントの指定は CSS2.1 で廃止されていましたが、後方互換のためか多くの Web ブラウザーで引き続き使用することができました。ただし、Web ブラウザー間でサポートされているフォントのフォーマットが異なったり、異なるドメインにあるフォントを使用できなかったりと、あまり実用的とは言えませんでした。

 

Web フォントのフォーマット

Webフォントのフォーマットとしては、以前から EOT(Embedded OpenType)、SVG(Scalable Vector Graphics)、SVGZ(gzip 圧縮されたSVG)、TrueType、OpenType などがありますが、今後標準として扱われるのは WOFF(Web Open Font Format) と言われています。

WOFF は、Mozilla Corporation や Type Supply、LettError など多くのフォントファンダリが共同で提案したウェブ専用の新しいフォントフォーマットであり、HTML5 対応をうたう最近 Web ブラウザーであればサポートされています。逆に、新しいフォーマットなので、ちよっと前の Web ブラウザーでは使用できません。

WOFF のフォーマットは、TrueType または OpenType フォントをテーブル単位で圧縮して、独自のヘッダーを追加した形式、ということで、TrueTyep Font から WOFF を生成するツールなんかも存在しますが、フォントの配布にはライセンスの問題も絡むと思うので、たとえば、個人でローカルにインストールしてあるお気に入りのフォントから WOFF を生成して Web で使用する際には、元になるフォントのライセンスを充分に確認したほうが良いでしょう。

また、Web には 有償/.無償で公開されている Web フォントがいくつもあるので、それらを利用する方が面倒がなくて良いでしょう。

いかにいくつかのサイトを列挙します。

Google web fonts
http://www.google.com/webfonts

デコもじ
http://decomoji.jp/

Typekit
http://typekit.com/

Fonts.com Web Fonts
http://webfonts.fonts.com/

Fontspring
http://www.fontspring.com/

Font Me Up
http://fontmeup.com/

和文フリーフォント集
http://book.fontgraphic.jp/fontbloglist/49-woff.html

 

Web フォントの使い方

「ハートブレイク・カフェ」のロゴは、Google web fonts 提供の「Lobster」 というフォントを使用しています。

使い方は非常に簡単で、以下のように <link> タグで Web フォントを指定して、CSS の font-family にフォント名を指定するだけです。

以下のサンプルでは、「Lobster」 以外に 「Wire One」と「Monofett」という Web フォントを使用しています。なお、href に google さんのサーバーを指定していますが、個別にフォントをダウンロード入手して使用することも可能なようです。(ここらへんは各フォントのライセンスを確認してください。)

<html >
<head>
    <title>Web Fonts Test</title>

    <!-- Lobster フォントの指定-->
    <link href="http://fonts.googleapis.com/css?family=Lobster&amp;subset=latin" rel="stylesheet"
        type="text/css" />

    <!-- Lobster Wire One フォントの指定 -->
    <link href="http://fonts.googleapis.com/css?family=Wire One&amp;subset=latin" rel="stylesheet"
        type="text/css" />

    <!—Monofett フォントの指定 -->
    <link href="http://fonts.googleapis.com/css?family=Monofett&amp;subset=latin" rel="stylesheet"
        type="text/css" />
</head>
<body>
    <h1 style="font-family:Lobster">The HeartBreak Cafe*</h1>
    <h1 style="font-family:Wire One">The HeartBreak Cafe*</h1>
    <h1 style="font-family:Monofett">The HeartBreak Cafe*</h1>
</body>
</html>

( Web フォントを指定した HTML サンプル )
 

image

( 異なる Web フォントの指定で 「The HeartBreake Cafe*」を表示したところ)

 

上記のように、いままでは画像で作成していたような、デザイン性の高いフォントも、Web フォントを使用すれば CSS の記述だけで行うことができ、HTML5 対応をうたう最新の Web ブラウザーであれば同じように表示されます。

フォントを変更するだけでも、ページの印象はまったく異なりますので、ぜひ使用してみてください。

そして、こういった最新の技術が当たり前に使用できるようになるよう、周りの方にも積極的に新しい Web ブラウザーの導入を奨めていただければと思います。

次回は radius について書きたいと思います。

 

 

Web Statistics

ライブコーディングを補助するためのツール

$
0
0

早いもので、もう 2011 年も終わりですね。

年末年始になると新年会や忘年会、クリスマス・パーティーというように、何かと集まりごとが増え、おのずとライブ・コーディングを披露する機会も多くなってくると思います。

マイクロソフトでは、Visual StudioExpression Webといった、HTML と JavaScript、およびCSS に対し強力な入力支援機能を持った製品をリリースしておりますので、それらを使用してライブ・コーディングを行うぶんにはそれほど苦労することはないでしょう。

しかし、現在の WebMatrix(※) や、テキストエディターでコーディングを行おうとすると、自力でコードをタイプしていく必要があります。

(※) WebMatrix 2からは HTML5, CSS, JavaScritp に入力支援機能(インテリセンス)が提供されます。

タイプのスピードが猛烈に早く、技芸と呼べるほどの速度でコードを記述することができるのであれば、観客を退屈させずに済むでしょうが、そういった芸当は誰にでもできるというわけではありません。

また、そういたタイプの達人といえども SVG や Canvas で使用する数値データを、ミスすることなく短時間で記述していくことはほぼ不可能といってよいでしょう。

そういった問題にある程度の解決策を提供すべく、簡単な入力支援用のツールを作成しましたのでシェアさせていただきます。

ちなみに、このツールは、先日「HTML5 プログラミング生放送勉強会 第11回@大阪」に登壇させていただく際、デモで使用する MebNatrix2 のコードスニペット プラグインが、β 段階のためか、動作しなくなってしまい、同様の機能を持ったツールを、大阪行きの新幹線の中で作成したものに設定機能を追加したものです。

名前は特にありませんが、ここで「スニペットツール」という表記を使用してアプリケーションの機能説明を行いたいと思います。

 

スニペットツールについて

スニペットツールは、アプリケーションのメニューを介して、あらかじめ用意しておいたテキストファイルの内容をクリップボードにコピー、または、任意のアプリケーションのエディター画面に貼り付けを行うものです。

スニペットツールが Window ハンドルを取得することのできるアプリケーションであれば、アプリケーションに対し貼り付けを行うことができます。

Windows ハンドルが取得できない場合は、クリップボードにデータを送ったままにしますので、アプリケーション側で貼り付けを行うことができます。

 

ダウンロードは以下から行ってください。

 

実行ファイルと設定ファイルのみ

ソースコード

(※) Windows 7 上の Visual Studio 2010 + .NET Framework 4.0 で作成していますが、.NET Framework 4.0 固有の機能は使用しておりませんので、ソースを貼りなおせば古い Visual Studio でもコンパイル可能です。

 

インストールの準備

.NET Framework 4.0 がインストールされていることを確認してください。

なお、.NET Framework 4.0 は以下から入手することができます。

 

.NET Framework 4 ダウンロード
http://www.microsoft.com/ja-jp/net/netfx4/download.aspx

 

インストール

snippets.zip を解凍し、以下の 2 つのファイルを同じフォルダーに配置してください。

Snippets.exe
Snippets.exe.config

次に Snippets フォルダをマイドキュメント (Windows 7 では 「ドキュメント��) にコピーします。

Snippets フォルダにはサンプルのスニペット用テキストファイルが含まれています。

以上でインストールは完了です。

アプリケーションを起動するには Snippets.exe をダブルクリックします。

 

スニペットツールを既定の状態で使用してみる

スニペットツールの既定の設定では、テキストファイルの内容をメモ帳に貼り付けを行うように設定されているので、メモ帳 (notepad) を起動します。

その後、Snippets.exe をダブルクリックしてスニペットツールを起動します。

スニペットツールは、起動時に貼り付け先の Window ハンドルを取得しにいくので、アプリケーションを先に起動しておく必要があります。

アプリケーションを後から起動した場合は、後述する設定ダイアログを表示して [OK] ボタンをクリックすることで、貼り付け先アプリケーションを認識させることができます。

スニペットツールが起動したら [★] マークをクリックし、メニュー [1.このアプリケーションについて]を選択してください。

あらかじめ起動しておいたメモ帳に文字列が貼り付けられるのを確認してください。

 

メニューを編集するには

マイドキュメント下の Snippets フォルダにテキストファイルを追加するか、既存のファイルを削除、あるいは編集します。

スニペットツールのメニューに表示されているのは、マイドキュメント下の Snippets フォルダ内にあるテキストファイル名から拡張子を除いたものであり、メニューをクリックして貼り付けられるのは、選択されたファイルの内容になります。

この使用は WebMatrix 2 のコードスニペットプラグインと共通です。

 

設定を変更するには

[★]マークの上で、マウスの右ボタンをクリックし、表示されたコンテキストメニューから [設定] を選択します。

image

[設定] ダイアログボックスが表示されるので、任意の設定を適宜行います。

image

① : メニューに表示させるテキストファイルが格納されているフォルダまでのパス
② : アプリケーションに貼り付けを行うかどうかのチェック 
      チェックを外すと、テキストファイルのデータはクリップボードにのみ送られます
③ : ウィンドウハンドルの取得をプロセス名で行うかキャプションで行うかのチェック
      アプリケーションを複数起動している場合、[プロセス名] でウィンドウの検索を行うと、
      一番最初に検出された同じプロセス名のウィンドウに貼り付けを行います。
      貼り付け対象となるウィンドウを明示的に指定したい場合は、[ウィンドウ キャプション] を
      指定します。
④ : 貼り付け対象となるアプリケーションのプロセス名か、ウィンドウキャプションを指定します
     なお、プロセス名を指定する際は、拡張子 (exe) は含めないでください。

 

以上で、スペットツールの説明は完了です。

機能の追加やデバッグ等は、ソースをダウンロードして各自自由に行っていただければと思います。

それでは皆様、また来年。

良いお年を。

 

 

Web Statistics

[実用HTML5API]ローカル ストレージへの画像のキャッシュ方法

$
0
0

最近、HTML5 API を使用した機能実装のサンプルを書く機会が増えてきたので、たまったところから数回に別けて書いていこうと思います。

さて今回のネタは、画像を Web ブラウザーの Window オブジェクトが提供する DOM ストレージのローカルストレージに保存する (キャッシュ) 方法についてです。

 

画像ファイルをキャッシュすることの利点

Web コンテンツでは以前から、写真などのビジュアルなコンテンツはもちろん、文字では表現の難しい内容の説明、あるいはページを装飾の目的で画像が使用されてきました。

時代は進み、回線の速度が向上してくると、コンテンツのリッチ化が進み、それに比例するように使用される画像の量は増える傾向にあります。

また、最近では高精細ディスプレイの普及、画面サイズの大型化などにより、画像のクォリティも高いものが使用されるケースが増えてきています。

しかしながら、回線の速度は常に高い状態を保障されているわけではないため、回線の状態、アプリケーションの構造によっては、画面の読み込みに時間がかかり、なかなか使用できないなど、ストレスを感じることもあるでしょう。

画像ファイルを Web クライアントのローカルストレージに保存し、これを <img> タグから参照させることで、画像の読み込みにかかる時間を軽減させることができます。

 

ローカルストレージとは?

Web ブラウザーの提供する DOM ストレージの一つで、セッション別のデータまたはドメイン固有のデータを名前/値ペアとしてクライアントに保存できます。

詳しくは、以下のドキュメントをご参照ください。

 

DOM ストレージの概要
http://msdn.microsoft.com/ja-jp/library/cc197062(v=VS.85).aspx

 

ローカルストレージに画像データを保存する方法

DOM ストレージが提供するセッションストレージ、ローカルストレージが保存できるのはテキスト形式のデータのみで、画像のようなバイナリーデータは、そのまま保存することができません。

バイナリーデータをテキストデータに変換する方法として Web で一般的に使用されているのが、Base 64 エンコードです。

Base 64 でエンコードした画像データであれば、以下のように <img> タグの src 属性に指定することができます。

<img id="Img1"

        alt="" src="data:image/jpg;base64,/9j/

                  4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAg(略)

                  vh6oV6laiaWiJaIloi/9k=" />

つまり、Base 64 エンコードされたデータをローカルストレージに保存し、画像を表示する際には、JavaScript を使用して <img> タグの src 属性に保存したデータをセットしてあげれば良いのです。

document.getElementById("pic01").src = window.localStorage.pic01Base64;

ローカルストレージの指定は、以下のように書くこともできます。保存場所の名前が変わる場合は、以下のように書のが良いでしょう。

document.getElementById("pic01").src = window.localStorage[“pic01Base64”];

 

画像ファイルを JavaScript で Base 64 エンコードするには?

画像ファイルがあらかじめ Base 64 でエンコードされており、JavaSctipt を使用してサーバーサイドから任意に取り出せればスマートなのですが、サーバーサイドの開発にはいささかの工数がかかるのと、状況によってはサーバーサイドの開発が行えない場合もあるでしょう。

そういった場合は、JavaScritp 側で Base 64 エンコードを行う必要がありますが、標準的な JavaScript には Base 64 エンコードを行う機能は用意されていません。

プラグインや、親切な誰かさんが作った OSS の js ライブラリーを使用するのも良いのですが、HTML5 をサポートするモダン Web ブラウザーでは Canvas オブジェクトの toDataURL メソッドを使用して、Base 64 エンコードを行うことができます。

以下は、Canvas オブジェクトの toDataURL メソッドを使用して Base 64 エンコードを行い、ローカルストレージに保存するコードです。

//キャンバスを作成
var canvas = document.createElement("canvas");
//コンテキストを生成
var ctx = canvas.getContext("2d");
//イメージオブジェクトを生成
var image = new Image();
//image オブジェクトに画像が読み込まれた際のイベントハンドラを登録
image.addEventListener("load",
              //イベントハンドラ
              function () {
                      //image に読み込んだ画像を描画する
                      ctx.drawImage(image, 0, 0);
                      //Canvas から DataURL (Base64 データ) を取り出しローカルストレージに保存
                     window.localStorage["pic01Cache"] = canvas.toDataURL();
                  }, false);

//image オブジェクトに画像をロード
image.src = "img/one.png";

画像を表示する際は、以下のように <img> タグの src 属性にローカルストレージの内容を指定すれば OK です。

//<img> タグの SRC に DataURL を指定
document.getElementById("pic01").src = window.localStorage["pic01Cache"];

以下に全体的なサンプルを示します。

<!DOCTYPE html >
<html>
<head>
    <title></title>
    <script type="text/javascript">
        (function () {

            //キャッシュをクリアする場合
            //window.localStorage.clear();

            //ドキュメントのロードが完了した際のイベントを登録
            window.addEventListener("load", loadImg, false);

            //イベントハンドラ
            function loadImg() {
                //ローカルストレージに目的のデータがあるか確認
                if (window.localStorage["pic01Cache"]) {
                    //存在すればそれを使用する
                    document.getElementById("pic01").src
                         = window.localStorage["pic01Cache"];
                    document.getElementById("msg").innerText
                         = "キャッシュされたイメージを使用しました。";
                }
                else {
                    //ローカルストレージにデータがない場合
                    //Canvas を作成
                    var canvas = document.createElement("canvas");
                    //描画コンテキストを取得
                    var ctx = canvas.getContext("2d");
                    //イメージオブジェクトを生成
                    var image = new Image();

                    //image オブジェクトに画像が読み込まれた際のイベントハンドラを登録
                    image.addEventListener("load",
                    //イベントハンドラ
                        function () {
                            //image に読み込んだ画像を描画する
                            ctx.drawImage(image, 0, 0);
                            //Canvas から DataURL (Base64 データ) を取り出し
                            //ローカルストレージに保存
                            window.localStorage["pic01Cache"] = canvas.toDataURL();
                            //<img> タグの SRC に DataURL を指定
                            document.getElementById("pic01").src 
                                 = window.localStorage["pic01Cache"];
                        }, false);

                    //image オブジェクトに画像をロード
                    image.src = "img/one.png";
                    document.getElementById("msg").innerText
                         = "イメージをキャッシュしました。";
                }
            }
        })();
    </script>
</head>
<body>
    <img src="#" alt="Alternate Text" id="pic01"/>
    <br />
    <div id="msg"></div>
</body>
</html>

画像一つをキャッシュするのにけっこうな行数があるように思われるかもしれませんが、コメントと、行の折り返しの結果そのように見えているだけです。

実際のところ処理自体は大したことはしていません。

また、これを応用して、ページ全体で使用する複数個の画像をキャッシュさせることももちろん可能ですが、読みこんだ画像ファイルを Canvas に描画する (表示はされません) メソッドは、非同期で動作するため工夫が必要です。

実は、複数ファイルをキャッシュするサンプルコードも書いたのですが、ある特定のモダン Web ブラウザーでのみ挙動が安定しないので、掲載を断念しました。非常にだんねんです。

複数ファイルのキャッシュについてはいろいろ工夫してみていただければと思います。

 

 

Web Statistics

[実用HTML5API]マニフェストファイルを使用したキャッシュ(Application Cache)

$
0
0

前回の投稿でローカルストレージに画像ファイルを保存してキャッシュするという、いささか珍妙な方法を紹介しましたが、やはり HTML5 のキャッシュといえば Application Cache だろうということで、こちらの方法も紹介しておきます。(と、いうか普通はこっちが先ですね)

ただし、この方法、残念なことに Internet Explorer 9 では動作しませんので、この機能を試すには Internet Explorer 10 の最新の DP か、その他のモダン Web ブラウザーをお使いいただければと思います。

 

Application Cache の使い方

Web コンテンツで Application Cache を利用するには、マニフェストファイルというものを用意し、キャッシュしたいファイルを列挙して記述します。

例えば以下のような、拡張子 appcache (※) というファイルを用意して、キャッシュさせたいファイルを記述します。

ファイル : myPage.appcache
CACHE MANIFEST
js/myPage.js
css/myPage.css
nextPage.htm
img/one.png
img/two.png
img/three.png
img/four.png
img/five.png

(※) アプリケーションキャッシュで使われるマニフェストファイルの推奨拡張子は、以前は .manifest でしたが現在は .appcache へと変更されています少し前のドキュメントでは .manifest ファイルが指定されていますが、内容的に異なる点はとくにありません。

キャッシュさせたいファイルは、より明示的に CACHE: の後に記述してもいいですし、逆にキャッシュさせたくない、サーバーサイドで動作するページなどは NETWORK : の後に記述します。

なお詳しくは、以下のドキュメントをご参照ください。

5.6.3 The cache manifest syntax
http://www.w3.org/TR/html5/offline.html#manifests

 

Web サーバー側で、拡張子 appcache に対する MIME 設定として “text/cache-manifest” を指定します。ちなみに、MIME タイプと拡張子が結びついていれば OK だそうで、拡張子を任意のものに変えても動作するそうです。

image

 

HTML ファイルでは、以下のように <html> タグの manifest 属性マニフェストファイルを指定します。

<!DOCTYPE html > 
<!-- マニフェストファイルの指定 -->
<html manifest="myPage.appcache">
<head>
    <title></title>
    <link href="css/myPage.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <a href="next.htm">contenlt</a>
    <br>
    <img src="img/one.png" alt="" />
    <br />
    <img src="img/two.png" alt="" />
    <br />
    <img src="img/three.png" alt=""/>
</body>
</html>

以上で Application Cache の設定は完了です。

この設定により、マニフェストファイルに指定された URL に対しては、一旦接続したあとは、リクエストが飛ばなくなります。

ただし、Web サーバーのリクエストログを読む限り、[F5] キーを押下するなどの、意図的に更新をおこった場合はクエストが発生するようです。

 

キャッシュされたコンテンツを更新するには

Application Cache では、マニフェストファイルになんらかの変更があった場合、Web ブラウザーが更新を検知して一連のキャッシュ作業をやり直します。

しかし、このマニフェストファイルの更新の検知は、一部のスマートフォンブラウザーではサポートされておらず、Web ブラウザーの設定でキャッシュをクリアする必要があるなど、コンテンツの提供者側でコントロールできない状況が生まれます。また、Web ブラウザーの検知機能に頼らず、意図的にキャッシュの内容を更新したい場合もあるでしょう。

そういった場合には、ApplicationCache オブジェクトを使用して JavaScript からキャッシュの更新作業行うことができます。

 

JavaScript からのキャッシュの更新

単純にキャッシュをクリアしたいのであれば、ApplicationCache オブジェクトの提供する swapCache (キャッシュの入れ替え) メソッドと reload (リロード) メソッドを続けて発行すれば OK です。

ただ、ApplicationCache オブジェクトには、マニフェストファイルが更新されたかどうかを判断するプロパティやメソッド、イベントハンドラーが用意されているので、それらと組み合わせても良いでしょう。

たとえば

//更新をチェック
window.applicationCache.update();
//キャッシュがアップデート可能かどうか比較
if (window.applicationCache.status ==window.applicationCache.UPDATEREADY)
window.applicationCache.swapCache();
window.location.reload();

または

window.applicationCache.update();
//イベントハンドラーの指定
window.applicationCache.addEventListener("updateready", function() {
        if (confirm("アップデートしますか?")) {
            window.applicationCache.swapCache();
            window.location.reload();
        }
    }, true);

などです。

 

その他、Internet Explorer 10 でサポートされている Application Cache については、以下のドキュメントをご覧ください。

 

Internet Explorer デベロッパーセンター Application Cache API ("AppCache")
http://msdn.microsoft.com/ja-jp/library/ie/hh673546.aspx#Application_Cache_API_AppCache

 

先日 (というか昨日) 投稿したローカルストレージと組み合わせ、Web コンテンツの表示の高速化 (キャッシュ)、オフライン機能の実装など、いろいろと応用いただければと思います。

 

Web Statistics

Developer Camp (HTML+JavaScript) 実践編フォローアップ

$
0
0

5 月 9 日の秋葉原 UDX で行われました Developer Camp - 【JavaScript 開発者のための Metro スタイル アプリ実践編】 にご参加いただきました皆様ありがとうございました。

私のセッションで紹介いたしました技術ドキュメントと SDK サンプルへのリンクを掲載させていただきます。(

(※) Windows Developer DaysWA005WA007で紹介したドキュメントのリンクの紹介は、これからやります。(スミマセン)

 

[はじめに]

��ベロッパー センター - Metro スタイル アプリ
http://msdn.microsoft.com/ja-jp/windows/apps
Windows 8 Metro スタイル アプリ開発者ポータル

 

Metro スタイルの設計原則
http://msdn.microsoft.com/library/ja-jp/windows/apps/hh781237
Metro スタイルアプリの作成に入る前に必ず読んでおくべきドキュメント
Metro スタイル アプリの UX ガイドライン」も必読

 

Windows Internet Explorer 10 開発者向けガイド
http://msdn.microsoft.com/ja-jp/ie/hh673549.aspx
Windows Internet Explorer 10 がサポートしている HTML5, CSS3 機能について

 

Hoands On: Windows 8 HTML5 Platform
http://ie.microsoft.com/testdrive/Graphics/hands-on-css3/
Windows 8 でサポートされている HTML5、CSS3 のデモコンテンツ

 

HTML and DOM reference
http://msdn.microsoft.com/en-us/library/windows/apps/br212882.aspx
Metro スタイルアプリの DOM リファレンス

 

[コントロールと UI]

Adding controls and content
http://msdn.microsoft.com/en-us/library/windows/apps/hh465393.aspx

 

Adding and styling controls
http://msdn.microsoft.com/en-us/library/windows/apps/hh465352.aspx
HTML Control、WinJS Control の追加、使用方法についてのリンクを含んでいます。

 

HTML essential controls sample
http://code.msdn.microsoft.com/Common-HTML-controls-and-09a72a24
HTML コントロール サンプルアプリ

 

HTML Rating control sample
http://code.msdn.microsoft.com/Rating-control-sample-4666c750

 

WinJS.UI.SemanticZoom object
http://msdn.microsoft.com/en-us/library/windows/apps/br229690.aspx
セマンティックズームについて

 

How to use templates to bind data
http://msdn.microsoft.com/en-us/library/windows/apps/hh700356.aspx
データバインドの方法について

 

HTML ListView essentials sample
http://code.msdn.microsoft.com/ListView-basic-usage-sample-fcc451db
ListView サンプルアプリ

 

Quickstart: adding an app bar with commands
http://msdn.microsoft.com/en-us/library/windows/apps/hh465309.aspx
アプリバーの追加について

 

HTML AppBar control sample
http://code.msdn.microsoft.com/App-bar-sample-a57eeae9
アプリバーのサンプルアプリ

 

ピクセルを最大限に活用する - 表示状態の変更に適応する
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/04/25/getting-your-pixels.aspx
スケーリングについて

 

Scaling according to DPI sample
http://code.msdn.microsoft.com/Scaling-sample-cf072f4f
スケーリングのサンプルアプリ

 

[コントラクト]

アプリで Windows 8 のコントラクトのアクティベーションを行う
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/03/30/windows-8-activating-contracts.aspx

 

Quickstart: Adding search to a Metro style app
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh465238.aspx
検索コントラクトの追加

 

Search sample
http://code.msdn.microsoft.com/windowsapps/Search-app-extension-sample-6baa6270

検索コントラクト サンプルコード

 

Sharing content
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh758315.aspx
共有コントラクトについて

 

Sharing content source app sample
http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Source-App-d9bffd84
共有ソース サンプルコード

 

Sharing content target app sample
http://code.msdn.microsoft.com/Sharing-Content-Target-App-e2689782
共有ターゲット サンプルアプリ

 

Quickstart: Adding app settings using Windows Runtime
http://msdn.microsoft.com/en-us/library/windows/apps/Hh770547.aspx
アプリケーションの設定について

 

App settings sample
http://code.msdn.microsoft.com/App-settings-sample-1f762f49
設定コントラクト サンプルアプリ

 

[ライフタイム サイクル]

アプリに対する “常に動作している” 感覚を重視したアプリ ライフサイクルの管理
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/04/16/managing-app-lifecycle.aspx

 

App activation and suspension sample
http://code.msdn.microsoft.com/windowsapps/App-activating-and-ec15b168
アプリのサスペンドとアクティブ化のサンプルアプリ

 

Introduction to Background Tasks
http://www.microsoft.com/en-us/download/details.aspx?id=27411
バックグラウンド処理の技術書

 

Background task sample
http://code.msdn.microsoft.com/windowsapps/Background-Task-Sample-9209ade9
バックグラウンドタスク のサンプルアプリ

 

Lock screen apps sample
http://code.msdn.microsoft.com/windowsapps/Lock-screen-apps-sample-9843dc3a
ロックスクリーンに表示されるアプリのサンプル

 

[タイル]

優れたタイル エクスペリエンスを開発する (パート 1)
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/04/20/10296001.aspx

 

優れたタイル エクスペリエンスを開発する (パート 2)
http://blogs.msdn.com/b/windowsappdev_ja/archive/2012/04/24/10297318.aspx

 

App tiles and badges sample
http://code.msdn.microsoft.com/windowsapps/App-tiles-and-badges-sample-5fc49148
タイルのサンプルアプリ

 

Secondary tiles sample
http://code.msdn.microsoft.com/windowsapps/Secondary-Tiles-Sample-edf2a178
セカンダリ タイルのサンプルアプリ

 

Quickstart: Sending a toast notification
http://msdn.microsoft.com/en-us/library/windows/apps/hh465448.aspx
トースト通知について

 

Toast notifications sample
http://code.msdn.microsoft.com/windowsapps/Toast-notifications-sample-52eeba29
トースト通知のサンプルアプリ

 

Metro スタイルアプリ管理サイト
https://manage.dev.live.com/build
Windows Push Notifications & Live Connect の登録など

 

How to authenticate with the Windows Push Notification Service (WNS)
http://msdn.microsoft.com/en-us/library/windows/apps/hh465407.aspx
Windows Push Notification Service を利用する際の認証の方法

 

Push and periodic notifications client-side sample
http://code.msdn.microsoft.com/windowsapps/Push-and-periodic-de225603
Push notifications クライアントのサンプルアプリ

 

Quickstart: Sending a tile push notification
http://msdn.microsoft.com/en-us/library/windows/apps/hh465450.aspx
タイルへのプッシュ通知について

 

[タッチファースト]

Pointer and gesture events
http://msdn.microsoft.com/ja-JP/library/hh673557.aspx
ポインターとジェスチャーの取得について

 

Gestures, manipulations, and interactions
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh761498
ジェスチャーとマニュピレーションについて

 

すべてのブラウザーでマルチタッチ入力とマウス入力を処理する
http://blogs.msdn.com/b/ie_jp/archive/2011/12/09/10245984.aspx

 

Events
http://msdn.microsoft.com/en-us/library/windows/apps/hh831233.aspx
Metro スタイルアプリの DOM イベント一覧

 

Input: DOM pointer and gesture event handling sample
http://code.msdn.microsoft.com/windowsapps/Input-DOM-pointer-and-2e5697ed
タッチとジェスチャーイベントを取得するサンプルアプリ

 

Input: manipulations and gestures using JavaScript sample
http://code.msdn.microsoft.com/windowsapps/Manipulations-and-gestures-26918bb3
マニュピレーションとジェスチャーのサンプルアプリ

 

以上です。

 

 

Web Statistics

[IIS]画像のフォーマットとサイズを変換して返すサービス

$
0
0

久しぶりに IIS についての記事を書きます。

先日、上司と話しをしていたら、Windows ストア アプリを作ってくださっているパートナーさんが、「IIS には画像のフォーマットやサイズを変換してくれるサービスがないのでわざわざ別のサーバーを立てている」との話を聞きました。

.NET の Bitmap クラスを使用したことがある人であればご存知の通り、画像のフォーマットの変換もサイズの変更も Bitmap クラスを使用すれは簡単に実現することができます。

IIS は .NET Framework で動作する ASP.NET のアプリケーションサーバーの機能を持っているので、その機能を組み込むことなどはわけはありません。

それこそ、鼻をほじりながらでも可能です。

ということで、今回は、私が実際に鼻をほじりながら(少しだけですが)作った、画像を変換するサービスのサンプルを紹介します。

 

IIS 7.x で画像のフォーマットとサイズを変換して返すサービスを作成する

今回の画像変換サービスを作成するにあたり、仕様は以下としました。

仕様

  1. 渡された URL の画像を取得し、加工した画像をレスポンスする
  2. パラメーターは元となる画像の 「URL」 と、変換する「画像フォーマット」、変換後の「幅」と「高さ」
  3. 幅、高さいずれかのパラメーターがない場合は画像のフォーマット変換のみ行う
  4. フォーマットが指定されていない場合は処理を行わない
  5. エラーの際にはなにもレスポンスを返さない (画像は抜けた状態となる)

開発は Visual Studio 2010、もしくは 2012 で行います。

プロジェクトは、シンプルにしたいので [Web サイト] で行い、ページを返す必要はないのでジェネリックハンドラを使用します。

具体的な手順は以下の通りです。

手順

  1. Visual Studio を起動
     
  2. メニュー [ファイル] – [新規作成] – [Web サイト] を選択
     
  3. [新しい Web サイト] ダイアログボックスが表示されるので、リストビューより [ASP.NET 空の Web サイト] を選択し [OK] ボタンをクリック
     
  4. [ソリューション エクスプローラー] から、作成された Web サイトを右クリックし、表示されたコンテキストメニューより [追加] – [新しい項目の追加] を選択
     
  5. [新しい項目の追加] ダイアログボックスのリストビューから [ジェネリックハンドラー] を選択し [追加] ボタンをクリック
     
  6. [ソリューション エクスプローラー] から、追加された拡張子 ashx のファイルをダブルクリック
     
  7. Visual Studio のテキスト エディタに ashx ファイルがオープンされるので、既定のコードを以下のように書き換え
     

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Net;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;

    //クラス名は作成した ashx の名前に合わせてください
    public class Handler : IHttpHandler {
       
        public void ProcessRequest (HttpContext context) {
            HttpRequest request = context.Request;
            HttpResponse response = context.Response;

            //変換する画像の URLを取得
            string imageURL = request.QueryString["u"];

            //変換する画像のフォーマットを取得
            string responseFormat = request.QueryString["f"];

            //クエリーストリングをイメージフォーマットに
            ImageFormat imgFormat = selectImageFormat(responseFormat);

            if (imgFormat == null || imageURL == "") return;
            //変換後の画像の幅を取得
            int responseImgWidth = nullToZero(request.QueryString["w"]);
            //変換後の画像の高さを取得
            int responseImgHeight = nullToZero(request.QueryString["h"]);

            try
            {
                byte[] buff = changeFormat(getImgStream(imageURL), responseImgWidth, responseImgHeight, imgFormat);
                response.ContentType = "image/" + responseFormat;
                response.Flush();
                response.BinaryWrite(buff);
                response.End();
            }
            catch(Exception ex)
            {
                //エラーが発生したらなにもしない。もうほんと、なにもしない。ぜんっぜんしない。
                return;
            }

        }

        //画像のフォーマットとサイズを変更する
        private byte[] changeFormat(Stream imgStrm, int imgWidth, int imgHeight, ImageFormat imgFormat)
        {
            Stream changedStrm;
            byte[] pixelByteArray;
            bool noSizeFlg = (imgWidth == 0 && imgHeight == 0);
            Bitmap bmp;

            if (noSizeFlg)
            {
                //変換する画像サイズが指定されていない場合
                bmp = new Bitmap(Image.FromStream(imgStrm));
            }
            else
            {
                //変換する画像サイズが指定されている場合
                bmp = new Bitmap(Image.FromStream(imgStrm), imgWidth, imgHeight);
            }

            changedStrm = new MemoryStream();
            bmp.Save(changedStrm, imgFormat);
            changedStrm.Position = 0;
            long strmLength = changedStrm.Length;
            pixelByteArray = new byte[long strmLength];
            changedStrm.Read(pixelByteArray, 0, (int)long strmLength);
            return pixelByteArray;
        }

        //クエリーストリングの内容から画像のフォーマットを設定する
        private ImageFormat selectImageFormat(string imgformat)
        {
            switch (imgformat)
            {
                case "jpg": return ImageFormat.Jpeg;
                case "gif": return ImageFormat.Gif;
                case "bmp": return ImageFormat.Bmp;
                case "png": return ImageFormat.Png;
                case "tiff": return ImageFormat.Tiff;
                case "icon": return ImageFormat.Icon;
                default: return null;
            }
        }


        private Stream getImgStream(string url)
        {
            //WebRequestの作成
            WebRequest webreq = WebRequest.Create(url);

            //タイムアウトの設定
            webreq.Timeout = 3000;

            //プロキシを探さないように
            webreq.Proxy = null;

            //サーバーからの応答を受信するためのWebResponseを取得
            HttpWebResponse webres = (HttpWebResponse)webreq.GetResponse();

            //応答データを受信するためのStreamを取得
            Stream strm = webres.GetResponseStream();

            return strm;

        }

        //null を 0 に変換
        private int nullToZero(string imgSize)
        {
            return (imgSize == null) ? 0 : int.Parse(imgSize);
        } 
       
        public bool IsReusable {
            get {
                return false;
            }
        }

    }

                        
  8. キーボードの [Ctrl] + [S] キーを押下して保存

 

以上でサービスは完成です。キーボード [F5] キーを押下して実行することができます。

ただし、画像を加工するためには、処理に使用する情報をサービスに渡す必要があります。

このサービスでは、必要情報を URL に含められたクエリーストリングとして情報を受け取ります。

使用するクエリーストリングは以下の通りです。

  • u – URL エンコードされた処理対象となる画像の URL (※)
  • f - 変換する画像のフォーマット (※)

    指定可能な画像フォーマットの内訳は以下のとおりです。

  • jpg   – Jpeg 形式
  • gif    – gif 形式
  • bmp – ビットマップ 形式
  • png – Png 形式
  • tiff – Tiff 形式
  • icon - アイコン形式
  • h - 変換後の画像の高さ
  • w - 変換後の画像の幅
  • ※ 印は必須

    たとえば、画像 http://www.contso.com/pic/test.jpg を高さ、幅ともに 100 ピクセルの png 画像に変換するには以下のようなクエリーストリングをリクエストします。

     

    ?url=http%3a%2f%2fwww.contso.com%2fpic%2ftest.jpg&f=png&w=100&h=100

     

    リクエストする URL 全体の例は以下の通りです。

    例)

    http://localhost/imgexcg/handler.ashx?url=http%3a%2f%2fwww.contso.com%2fpic%2ftest.jpg&f=png&w=100&h=100

     

    作成したコードの配置について

    作成した画像変換サービスを実際の IIS7.x に配置するには、コードを記述した拡張子 ashx ファイルを ASP.NET アプリケーションが動作するように設定した IIS の仮想フォルダにコピーするだけです。

    ASP.NET アプリケーションが動作するように仮想フォルダを設定する具体的な手順については、以下の第六章の内容をご参照ください。

     

    インターネット Web サーバー構築ガイドライン (ドラフト版)
    http://msdn.microsoft.com/ja-jp/ff625168

     

    今回は手順をシンプルにするために、ソースコードをそのまま配置する方式としていますが、ASP.NET アプリケーション用のプロジェクトテンプレートを使用すれば dll  にすることもできます。

    詳しい方法については、お近くの ASP.NET に詳しい人に訊いてください。

    なお、Visual Studio をお持ちでない人のために、前出のサンプルコードを記載済の ashx ファイルを SkyDrive にアップしましたので、プログラムが書けない人もお試しください。

     

    Windows 8 上の IIS 8 での実行について

    今回紹介したコードは Windows 7 上の IIS7.5 で正常に動作することを確認しておりますが、Windows 8 上の IIS8 の既定の設定では正常に動作しません。

    このコードは、当初 Windows 8 + Visual Studio 2012 で作り始めましたが、インターネットからのファイルの取得がうまくいきませんでした。

    いろいろ確認してみたところ、私の手元の環境 (Windows 8 × 2 台) では、ASP.NET プロセスから WebRequest、WebClient を使用した場合、クロスドメインのリクエストが飛ばないことがわかりました。

    これが会社で設定されているグループポリシーの影響なのか、インストールされているアプリケーションの組み合わせの問題なのか、はたまた Windows 8 上の IIS の新しい仕様なのか、現在はまだよくわかりません。

    ちなみに、同僚に試してもらったところ Windows Server 2012 上の IIS8 + ASP.NET ではクロスドメインの呼び出しは問題なくできるようです。

    この Windows 8 上の IIS8 からクロスドメインへのリクエストが出ない原因については、わかり次第この記事に追記したいと思います。

    ----- ・ ----- ・ ----- ・ ----- ・ ----- ・ -----・ ----- ・ -----

    今回が今年最後のブログのポストになります。

    このブログも ASP.NET → IIS → Web → Windows ストア アプリと、投稿する記事のテーマが変わっていますが、たまには今回のように振り返って IIS 関連の記事も良いですね。

    まだまだ皆さんにお伝えしたいネタがありますので、来年もよろしくお願いいたします。

    それでは良いお年を。

     

     

    Web Statistics

    気軽に Web コンテンツを書くためのエディタ

    $
    0
    0

    新年あけましておめでとうございます。

    昨年は、Windows 8 リリースに向けての準備に始まり、リリースを終えて一年が終るという、私にとっては本当に Windows 8 一色の一年でした。

    Windows 8 は、リリースされたとはいえ、その新しい UI 上で動作する Windows ストア アプリはまだまだ少ない状況ですので、今後も引き続き、Windows ストア アプリの開発に関する情報をお届けしていく予定です。

    もちろん、Windows ストア アプリだけでなく Internet Explorer 10 や、たまには IIS8 の情報も出していければ、思っておりますのでご期待ください。

     

    さて、年が明けて第一回めの投稿となる今回のネタですが、Windows 8 には全く関係ない、自作ツールの紹介です。

    実は、3 年ほど前まで、外部のブログで、数回の記事にわけてテキスト エディタを開発し、C# と Windows デスク トップアプリケーションの作り方を学ぶ、というのをやっておりました。

    そのブログは最終回の直前で、私の業務内容が変わったこともあり、そのまま中断していたのですが、先日そのブログを読んでいたという方から「最終回の記事を書いてほしい」というリクエストがあり、つい一昨日、お正月明けの勢い(?)を借りて最終回の記事を投稿した次第です。

    その際に、当時作成したテキストエディタのソースを引っ張りだして来て、Visual Studio 2012 でのコンバートと、細かいコードの調整を行ったのですが、使ってみるとなかなか使い勝手がよく、ちょうど手軽に Web コンテンツを書くツールも欲しかったこともあり、その記事を投稿後に本格的に自分用に機能を追加してみた次第です。

    さすが、自分用に作っただけあって、使い勝手はすこぶる良いです(笑)

    使い方の紹介と、ソースコードもダウンロード可能としますので、興味のある方はそのまま使うなり改造して使うなりしてみてください。

    以降は、このテキスト エディタの機能の紹介になります。

    image

    メモ帳との違い

    テキストの編集機能は メモ帳 とまったく変わるところはありませんが、それ以外でさまざまな変換機能、入力支援機能などが追加されています。

    これら機能のほとんどは、このテキストエディタのメニュー [ツール] – [オプション] にまとめられています。

    image(メニュー[ツール]-[オプション]を展開したところ)

    以降これらの機能から メモ帳 にはない機能について説明していきます。

     

    扱うファイルの拡張子の追加と削除機能

    アプリケーションで編集対象とするファイルの拡張子を自由に追加/削除することができるようになっています。

    具体的には、ファイルを開くダイアログボックスの、ファイルの種類を示すドロップダウンリストの内容を任意で指定することが可能です。

    これにより未知の拡張子が付けられたテキスト形式のファイルを編集するときでも、いちいちアプリケーションのソースを変更してフィルタの種類を増やす必要はありません。


    extention

    (ファイルの拡張子を指定するダイアログボックス)

     

    image (ファイル選択ダイアログボックスの拡張子ドロップダウンボックス)

     

    もちろん、リストが多すぎると感じる場合は、拡張子のエントリーを削除することもできます。

     

    ファイルの暗号化

    ファイルを保存する際にファイルを暗号化する機能をサポートしています。

    encrypt

    (設定ダイアログボックスの[エンコードと暗号化]パネル)

     

    この機能は、テキストを肉眼で判読不能とする所謂 (いわゆる) 難読化とは異なるもので、NTFS の暗号化ファイルシステム (EFS: Encrypting File System) の機能を利用して、ファイルの作成者しかファイルを開けないようにします。

    この機能を使用すると、絶対に誰にも見られたくないような内容の文書を思う存分書いても安心して保存しておくことができます。

    ただし、この機能は、OS がユーザーアカウントを管理する機能を搭載していることが前提であるため、Windows XP の Home エディションなどでは使用することはできませんで注意が必要です。

    NTFS 暗号化の詳細についてさらに詳しく知りたい方は以下の内容をご参照くださいませ。

     

    暗号化ファイル システムの最善の使用方法
    http://support.microsoft.com/kb/223316/ja

     

     

    ファイルの自動保存

    編集中のファイルを一定間隔で保存する自動保存の機能をサポートしています。
    autoSave(設定ダイアログボックスの [自動保存] パネル)

     

    編集中のファイルに上書きするか、作業ファイルに保存するか選択することができます。

    作業ファイルに自動保存するようにした場合は、アプリケーションの exe のあるフォルダ下に自動的に作成される AutoSaved という名前のフォルダに作業中の内容が保存されます。

    AutoSaved フォルダ内のファイルは、ファイルの保存、アプリケーションの正常終了時に自動的に削除されますが、アプリケーションの異常終了などでファイルが残っている場合は、アプリケーションの次回起動時に作業ファイルの内容をロードするかどうかを聞いてきます。

    この機能を積極的に使用することにより、予期せぬアプリケーションの終了や、不意のマシン クラッシュといった不幸な出来事から受けるデータの損失と精神的なダメージを少なからず和らげることができます。

     

    バイナリファイルの Base 64 エンコードとデコード

    画像などの非テキストファイルを Base 64 エンコードし、テキストに変換する機能、そしてデコードして元のファイル形式に戻す機能を搭載してします。

    この機能を使用するには、アプリケーションのテキスト エディター部分に目的のファイルをドラッグします。

    confirm_encode

    (非テキスト形式のファイルがドラッグされたところ)

     

    テキスト エディター部分にテキスト ファイル以外のファイルをドラッグすると、Base 64 エンコードするかメッセージボックスが表示されるのでエンコードする場合は [OK] ボタンをクリックします。

    64Encoded

    (Base 64 エンコードされたデータ)

     

    たとえば、この機能を使用して、画像ファイルを Base64 エンコードし、以下のように HTML ファイルの <img> タグのソースとして埋め込むことができます。

    <img src="data:image/jpg;base64,xxxxxx...." />

    また単純に、所持しているたけで自らの社会的身分を危うくするようなファイルを分からないように文字列として保存しておくことにも使えるのかもしれません。

    メニュー[ファイル] - [名前を付けて保存] を選択すると、仮の拡張子 *.bs64 のついたファイル名が付けられますが、自分でもとのファイル名と、どのようなファイルであるかを覚えているのであれば変更してしまっても構いません。

     

    Base 64 エンコードができるからには、当然 Base64 エンコードされたデータをデコードして元に戻す機能も搭載しています。

    Base 64 エンコードされたデータを元のファイル形式に戻すには、メニュー[ファイル] - [Base64 デコードして保存] を選択します。

    Decode64

    (デコード用のメニューはファイルを Base 64 エンコードした際か、拡張子 *.bs64 ファイルをオープンした際に有効になる)

     

    なお、この時のファイル名が、このアプリケーションが付けたものであれば、自動で元のファイル名が付けられますが、変更している場合は、明示的に正しい拡張子を含んだ名前を付ける必要があります。

    この機能を使用して、Base 64 で保存しておいたファイルをもとのファイル形式に戻すことはもちろん、ネットワークから取得したデータから Base 64 部分を取り出し、うへへぇ~とか言いながら (べつに言わなくても構いませんが) 復元して内容を確認するといったことにも使用できます。

     

    これまで紹介してきた機能の他、メニュー [ツール] にもいくつかの特徴的な機能が実装されています。

    image

    (メニュー[ツール] を展開したところ)

    ここからはメニュー[ツール]からアクセスできる機能について紹介します。

     

    カラーコードの挿入

    メニュー [ツール] – [カラーコードの挿入] を選択すると、カラーパレットが表示され、色を選択することでそのカラーコードをドキュメント上に挿入できます。
     

    image

    (色を選択すめためのカラーパレット)

     

    挿入されるデータは、カラーネームを持つものであればカラーネームが、カラーネームを持たないものはカラーコードが挿入されます。

     

    URL, HTML エンコード/デコード

    メニュー [ツール] – [URL 文字列]、[HTML 文字列] をマウスでポイントすると、それぞれ URL エンコード/デーコード、HTML エンコード/デコード 機能を使用することができます。

    image(メニュー[URL 文字列]、[HTML 文字列]内には、それぞエンコード/デコードのメニューがある)

     

    エンコード/デコードは、テキストボックス内で、マウスで選択されている文字列に対して行われますが、マウスでなにも選択されていない場合は、テキストボックス全体がエンコード/デコードされます。

     

    入力支援機能

    このテキスト エディタは、非常に単純かつ合理的な入力支援機能 (スニペット) を備えています。

    具体的には、特定のフォルダ下に配置したテキストファイル名をメニューとして表示し、メニュー選択時には、そのテキストファイルの内容をエディター上に出力します。

    メニューの階層構造は、ユーザーが作成するディレクトリ構造が反映されます。

    階層の深さの制限は設けていませんので、アプリケーションが Stack overflow を起こして異常終了するまで階層を深くすることができます。

    image(Snippets フォルダに 「新春お試しセット」 を配置したもの)

     

    この機能を使用するには準備が必要です。

    アプリケーションは、初回起動時に自らの *.exe があるフォルダに Snippets という名前のフォルダを作成いるので、好みのメニューの階層構造を意識しながらフォルダを作成します。

    その後、よく使用するタグや、コードブロックを記述したテキストファイルを各フォルダに配置します。

    ファイルを配置後、アプリケーションを起動すると、メニュー [スニペット]下にメニューが作成され、配置した各ファイルの内容がテキストボックスに出力されます。

    なお、メニューに表示されるキャプションは、それぞれフォルダ名、拡張子を除いたファイル名になります。

     

    アプリケーションのプロジェクトファイル

    この記事で紹介したテキスト エディタのソースを以下 SkyDrive にアップしました。

    手元の環境で、Visial Studio 2010 と Visual Studio 2012 で開発できることを確認しています。

    なお、このアプリケーションは個人的に作成したツールの紹介となりますので、不具合の修正、機能追加のリクエストをいただいても対応できませんのであらかじめご了承ください。

    アプリケーション内のコードの変更配布、再利用は自由にしていただいて構いません。

    さらに今回、新春特別企画として、私が個人的に作成中のスニペットのイメージをアップしましたので、ダウンロード完了後、zip ファイルの中身を、アプリケーション フォルダ内の Snippets フォルダに展開し、お試しください。

    また、コンパイル済のものも用意しましたので Visual Studio をお持ちでない方はこちらをお試しください。(新春特別パック入り)

    お使いの Windows  OS に .NET Framework 3.5 以上がインストールされていれば、monoeditor.exe をダブルクリックししてそのまま動作するはずです。

    ということで、今年もよろしくお願いいたします。

     

    【2013 / 01 / 18 ぶんの修正と機能追加】

    その後、このエディターを使用して、この記事のカレンダーコントロールを作ってみましたが、文字化けなどが発生したり機能が足りなかったりしたので、更新してみました。

    大まかな変更点は以下との通りです。

     

    ファイル読出し/書き出し時の文字コードの変更

    前回までのコードでは、文字エンコードは TextBox コントロールの既定のままとしていましたが、どうやらファイルの文字エンコードに合わせた処理をよしなにやってくれるということは無いようで、日本語を含む html などを編集すると文字化けがはなはだしので、扱う文字エンコードを UTF8 で固定しました。

     

    Web 関連ファイル編集時のファイルドラッグ処理の変更

    拡張子が htm, html, aps, apsx, php のファイルを編集している時に、エディタのテキストボックス部分にファイルをドラッグした際、ドラッグされたファイルの種類に合わせ、ファイルへの相対パスを含む HTML タグを挿入するようにしました。

    なお、ドラッグするファイルは複数選択が可能です。

    ��理されるファイルと挿入される HTML タグの内訳は以下の通りです。

    拡張子 挿入されるタグ
    jpg, jpeg, gif, png <img>
    js <script>
    css<link>
    その他のファイル <a>

     

    この機能を使用すると、js ファイルや CSS ファイルへの参照も、目的のファイルをエディタにドラッグするだけでよく、いちいちタグを記述する必要はなくなります。

    (今にして思えば <video>、<audio> タグの挿入も対応しておけばよかったな、と。)

     

    追加された機能

    image

    メニュー[ツール] に [関連づいたアプリケーションで開く] 機能と、[作業中のフォルダを開く] 機能を追加しました。

     

    [関連づいたアプリケーションで開く] 機能

    編集中のファイルに、OS で関連付けられたアプリケーションでファイルをオープンします。

    この機能を使用すると、たとえば、HTML ファイルを編集中に変更結果を見たい場合、いちいち編集中のファイルのあるフォルダを開き、ファイルをダブルクリックして Web ブラウザを起動するといったアクションが必要なくなります。

     

    [作業中のフォルダを開く]

    編集中のファイルが保存されているフォルダを開きます。

    たとえば、編集中のファイルをコピーする場合など、いちいちフォルダのトップレベルから階層を下るということをしなくてもよくなります。

     

    上記に記載した内容は、ダウンロードできるイメージに既に反映されていますので、興味のある方はお試しください。

     

    Web Statistics

    HTML5 を使ったシンプルな 2 D ゲームの作り方 (画像のロード)

    $
    0
    0

    2 回前の記事から、去年末に出演した schoo (スクー) さんの授業で使用したサンプルアプリをもとにした、HTML5 を使ったシンプルな 2 D ゲームの作り方を紹介しています。どんなゲームを作るのかは 1 回目の記事の中に実際に動作するゲームが埋め込んであるのでぜひ遊んでみてください。

    1. HTML5 を使ったシンプルな 2 D ゲームの作り方(序)
    2. HTML5 を使ったシンプルな 2 D ゲームの作り方(準備編)

    さて、前回は Visual Studio 2013 でのプロジェクトの作成と必要なファイルの追加方法などを説明しましたが、3 回目となる今回は、いよいよ HTML タグと JavaScript コードを記述していきます。

    Visual Studio でのプロジェクトの作成やファイルの追加、ゲームで使用する画像の入手が済んでいない方は、2 回目の記事の内容に従い準備を行ってください。

     

    Canvas のタグの記述

    実際のゲームを描画して動かすための Canvas タグを body 内に記述します。

    Visual Studio ではコードスニペットという機能により canvas とタイプし、キーボードの [Tab] キーを押下するだけで canvas タグが追加されます。

    image

    続けて属性を追加します。以下の各属性を設定してください。

    属性
    id bg
    widht 320
    heiht 480
    style background-color:black;

     

    入力中はインテリセンスと呼ばれる入力支援機能が働くので、有効に利用してください。

    image 

    最終的に canvas タグは以下のようになります。

    <canvas id="bg" style="background-color: black" width=”320" height="480">
            この Web ブラウザーは Canvas をサポートしていません。
            最新のモダンブラウザを使用してください。 
    </canvas>

     

    Canvas タグ内の文字列は、Canvas をサポートしていないレガシーな Web ブラウザーに表示されます。

    この時点での default.html の HTML ソースは以下のようになっています。

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <link href="css/main.css" rel="stylesheet" />
        <script src="js/main.js"></script>
    </head>
    <body>
        <canvas id="bg" height="480" width="320" style="background-color:black;">
            この Web ブラウザーは Canvas をサポートしていません。
            最新のモダンブラウザを使用してください。
        </canvas>
    </body>
    </html>

     

    Canvas のサイズ指定について

    Canvas のサイズ指定は CSS ではなく、必ずタグの属性で指定する必要があります。

    それは Canvas の描画領域のサイズは、書式ので指定される「表示されるサイズ」ではなく「描画を行うための領域の確保」だからです。

    この描画領域の確保は、ブラウザが Canvas タグを解釈したと同時に行われます。よって、Canvas タグにサイズに対する指定が無いと、Canvas の既定の サイズである width 300px、height 150px で描画領域が確保されます。これに対し、CSS でサイズを指定すると、CSS で指定されたサイズで引き延ばすか圧縮された形になりアスペクト比が同じでない限り意図したとおりに描画されませんので注意が必要です。

    例えば以下は、左側の画像が Canvas の width 属性と height 属性でサイズを指定したもの、右側の画像が style 属性で width と height を指定したものです。
    右側の画像では、サイズとアスペクト比が意図しないものになっています。

    image

     

    Canvas への画像のロード

    前回の記事で追加した画像を Canvas に表示するための JavaScript を記述します。

    ソリューションエクスプローラーで js フォルダにある main.js をダブルクリックして開きます。

     

    ネームスペース汚染を防止するおまじない

    Canvas についての JavaScript コードを記述する前にネームスペース (名前空間) を汚染しないようにするための記述を行います。

    ネームスペースの汚染とは、ライブラリやフレームワークが使用している変数を自分のコードで使用している変数で上書きしてしまうことです。

    おまじないのようなものですが、きちんと理解したい方は以下のドキュメントの「名前空間を汚染しないための工夫」をご覧ください。

    以下のように即時実行関数を記述し、その中にこれからのコードを記述していきます。

    (function () {
        //この中にコードを記述していく
    })();

     

    今回の紹介するゲームのコードは、ライブラリ等を使用していないので、ネームスペースの汚染をそれほど心配しなくても大丈夫ですが、今後のためにおまじない的に記述する習慣をつけておくことをお勧めします。ただし、これでネームスペースの汚染をすべて防げるわけでもないということも覚えておいてください。(※ ネームスペースの汚染を最小限にする方法もありますが、今回はコードの可読性、わかりやすさを優先するので割愛します。)


     

    プログラム全体で使用する変数の宣言

    前の手順で記述した即時実行関数の中に、プログラム (ゲーム) 全体で使用する変数の宣言をします。ここで宣言した変数は、即時実行関数内のどこからでも参照できます。

    (function () {
    //プログラム全体で使用する変数
    var canvas = null;
        var ctx = null;
        var img_snow = null;
        var img_snow_man = null;

     
    })();

     

    画像をロードするめための関数

    画像をロードするための loadAssets 関数を記述していきます。

    Visual Studio を使用している場合は、func とタイプし キーボードの [Tab] キーを 2 回押下すると myfunction という空の関数が定義されるので、関数名を loadAssets  に書き換えます。以下のようになります。

    function loadAssets () {

    }


     

    loadAssets  関数の中身を以下のように記述します。(コメントは記述しなくて大丈夫です)

    Visual Studio が入力を助けてくれるのでぜひタイプして入力してみてください。

    //HTML ファイル上の canvas エレメントのインスタンスを取得 
    canvas = document.getElementById(‘bg');

    //2D コンテキストを取得
    ctx = canvas.getContext('2d');

    //image オブジェクトのインスタンスを生成
    img_snow = new Image();

    //image オブジェクトに画像をロード
    img_snow.src = '/img/snow.png';

    /*画像読み込み完了のイベントハンドラーに Canvas に
       画像を表示するメソッドを記述 */
    img_snow.onload = function () {
       //canvas 上で image を描画
        ctx.drawImage(img_snow, 0, 0); 
    };

     

    次に HTML 上のエレメントのロードが完了した際に loadAssets 関数を呼び出すイベントハンドラを記述します。


     

    コンテンツのロード完了時に動作する
    イベントハンドラの実装

    ページの準備が完了した際に発生するイベントとしては、windows オブジェクトの onload イベントが古くから使用されてきましたが、HTML5 からは DOMContentLoaded が使用できます。DOMContentLoaded イベントは、window.onload イベントと同じく、参照している CSS ファイルなどが読み込まれ、準備完了となった際に発生しますが、DOMContentLoaded イベントが発生するのは純粋に DOM のロード完了についてのみであり、window.onload イベントのように画像ファイルのロード完了までは待ちません。そのため DOMContentLoaded イベントを使用したほうが迅速に DOM への処理を開始することができます。今回の場合は当てはまりませんが、とくに画像の読み込みが遅いページでは、イベント発生までの時間が大きく異なります。

    このへんの詳細については以下のドキュメントをご参照ください。

    loadAssets  関数を呼び出す DOMContentLoaded  イベントハンドラを以下のように記述します。

    //DOM のロードが完了したら実行
    document.addEventListener("DOMContentLoaded", function () {
            loadAssets();
    });

     

    main.js ファイル全体の JavaScript コードは以下のようになります。

    (function () {  
       //プログラム全体で使用する変数
       var canvas = null; 
       var ctx = null;
       var img_snow = null;
       var img_snow_man = null;  

       //DOM のロードが完了したら実行  
       document.addEventListener("DOMContentLoaded", function () {
            loadAssets();
        });

        function loadAssets() {
            //HTML ファイル上の canvas エレメントのインスタンスを取得  
            canvas = document.getElementById('bg');

            //2D コンテキストを取得 
            ctx = canvas.getContext('2d');

            //image オブジェクトのインスタンスを生成 
            img_snow = new Image();

            //image オブジェクトに画像をロード
            img_snow.src = '/img/snow.png';

            /*画像読み込み完了のイベントハンドラーに Canvas に
               画像を表示するメソッドを記述 */
            img_snow.onload = function () {
                //canvas 上で image を描画 
                ctx.drawImage(img_snow, 0, 0); 
            };
        };
    })();


     

    Web ページの表示

    Visual Studio で Web ページを表示する方法はいくつかありますが、最も基本的な方法は、ソリューションエクスプローラーで Web ブラウザーで表示させるファイルを選択しておき、キーボードの [F5] キーを押下する方法です。

    上記の方法のとおり、ソリューションエクスプローラーで default.html を選択し、キーボードの [F5] キーを押下します。

    Internet Explorer が起動し、以下のように黒い Canvas の左上に雪の結晶の画像が表示されます。

    image

    エラーが発生した場合には、Visual Studio を使用してそのまま JavaScritp のコードをデバッグすることができます。

    Vusial Studio のデバッグ機能については以下のドキュメントをご参照ください。


     

    画像の表示位置の調整

    画像の表示位置は、context オブジェクトの drawImage メソッドの引数で指定します。

    このコードでは context オブジェクトは変数 ctx に格納されており、img_snow.onload  イベントハンドラ内で drawImage  メソッドを呼び出していますが、横位置(X 座標)、縦位置(Y 座標) を指定する第一、第二引数が両方とも 0 であるため画像が左上端に表示されています。

    //canvas 上で image を描画  
    ctx.drawImage(img_snow, 0, 0); 

     

    これを画面の中央に表示するようにしてみましょう。

    画像を Canvas の中央に表示するための X 座標は以下の式で求めることができます。

    x = (Canvas の幅 / 2)  - (画像の幅 /2 )

    この式を使用して画像を Canvas の中央に表示するための getCenterPostion 関数を以下のように定義します。

    //中央の Left 位置を求める関数
    function getCenterPostion(containerWidth, itemWidth) {
        return (containerWidth / 2) - (itemWidth / 2);
    };

     

    定義した getCenterPostion 関数の第一引数として Canvas の描画領域のサイズ、第二引数として表示する画像のサイズを指定することにより、対象画像を Canvas の中央に表示する際に指定すべき X の値が返ります。

    getCenterPostion 関数を使用して img_snow.onload  イベントハンドラ内を以下のように書き換えます。

    img_snow.onload = function () {
          img_snow._x = getCenterPostion(canvas.clientWidth, img_snow.width);
          img_snow._y = 0;
          //canvas 上で image を描画
          ctx.drawImage(img_snow, img_snow._x, img_snow._y);
    };

     

    default.html を選択し、キーボードの [F5] キーを押下してページを実行すると、以下のように雪の結晶の画像が中央に配置されます。

    image 

    雪だるま画像の追加

    雪だるまの画像を追加します。やりかたは雪の結晶の画像を追加したのと一緒ですので、該当部分のコードをコピーして貼り付け、変数名 img_snow を img_snow_man に、画像のパスを ‘/img/snow_man.png’ に書き換えます。

    具体的には以下のようになります。

    //(※) この上 ↑ に雪の結晶画像をロードするコードがある
    //雪だるま画像のロード
    var img_snow_man = new Image();
    img_snow_man.src = '/img/snow_man.png';
    img_snow_man.onload = function () {
          img_snow_man._x = getCenterPostion(canvas.clientWidth, img_snow_man.width);
          img_snow_man._y = 0; 
          ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
    };

     

    雪だるま画像は、表示領域の底辺に画像の底辺がつくように配置する必要があります。

    その際の Y 座標は、以下の式で求めることができます。

    y = Canvas の高さ – 画像の高さ

    これの式を使用して雪だるまを描画する img_snow_man.onload  イベントハンドラ内を以下のように書き換えます。

    img_snow_man.onload = function () {
                img_snow_man._x = getCenterPostion(canvas.clientWidth, img_snow_man.width);
                //雪だるま画像は、表示領域の底辺に画像の底辺がつくように
                img_snow_man._y = canvas.clientHeight - img_snow_man.height;
                ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
            };

     

    default.html を選択し、キーボードの [F5] キーを押下してページを実行すると、以下のように雪だるまの画像が Canvas の中央の底辺に配置されます。

    image

    main.js ファイル全体の JavaScript コードは以下のようになります。

    (function () {
        //全体で使用する変数
        var canvas = null;
        var ctx = null;
        var img_snow = null;
        var img_snow_man = null;

        //DOM のロードが完了したら実行
        document.addEventListener("DOMContentLoaded", function () {
            loadAssets();
        });

        function loadAssets() {
            //HTML ファイル上の canvas エレメントのインスタンスを取得 
            canvas = document.getElementById('bg'); 
            //2D コンテキストを取得
            ctx = canvas.getContext('2d'); 

            //image オブジェクトのインスタンスを生成
            img_snow = new Image(); 
            //image オブジェクトに画像をロード
            img_snow.src = '/img/snow.png'; 
            /*画像読み込み完了のイベントハンドラーに Canvas に
               画像を表示するメソッドを記述 */
            img_snow.onload = function () {
                img_snow._x = getCenterPostion(canvas.clientWidth, img_snow.width);
                img_snow._y = 0;
                //canvas 上で image を描画
                ctx.drawImage(img_snow, img_snow._x, img_snow._y);
            };

            //雪だるま画像のロード
            img_snow_man = new Image();
            img_snow_man.src = '/img/snow_man.png';
            img_snow_man.onload = function () {
                img_snow_man._x = getCenterPostion(canvas.clientWidth, img_snow_man.width);
                img_snow_man._y = canvas.clientHeight - img_snow_man.height;
                ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
            };
        };

        //中央に配置する画像の X 座標を求める関数
        function getCenterPostion(containerWidth, itemWidth) {
            return (containerWidth / 2) - (itemWidth / 2);
        };
    })();


     

    まとめ

    今回の記事では HTML に Canvas タグを追加し、JavaScript を使用してゲームで使用する画像を表示するところまでを紹介しました。

    次回は今回表示した画像にアニメーションを実装していきます。

     

    ⇒「HTML5 を使ったシンプルな 2 D ゲームの作り方 (アニメーションの実装)」へ

    Web Statistics

    HTML5 を使ったシンプルな 2 D ゲームの作り方 (アニメーションの実装)

    $
    0
    0

    去年末に出演した schoo (スクー) さんの授業で使用したサンプルアプリをもとにした、HTML5 を使ったシンプルな 2 D ゲームの作り方を紹介しています。

    どんなゲームを作るのかは 1 回目の記事の中に実際に動作するゲームが埋め込んであるのでぜひ遊んでみてください。なお、開発に必要な画像データは 2 回目の記事からダウンロードできまるので、実際にゲーム開発を体験したい方はそちらから入手してください。

    1. HTML5 を使ったシンプルな 2 D ゲームの作り方(序)
    2. HTML5 を使ったシンプルな 2 D ゲームの作り方(準備編)
    3. HTML5 を使ったシンプルな 2 D ゲームの作り方 (画像のロード)

    今回の記事では、前回の記事で Canvas に表示した画像を動くようにしていきます。

     

    Canvas のアニメーション

    Canvas でのアニメーションは、描画したオブジェクトのプロパティに現在位置と移動先の位置、時間あたりの移動量を指定で云々….というものではなく、もっと原始的な、わかりやすくいうとパラパラ漫画のようなものです。

    描画要素をオブジェクトとして扱える SVG とは違い Canvas で描画された画像は、単なるビットマップ (絵) なので描画された要素を個別に指定することはできません。つまり、Canvas 上に UFO を描画した場合、表示の済んだ UFO をオブジェクトとして指定することはできません。たとえその UFO がアダムスキー型であろうが葉巻型であろうが、はたまたオーソドックスな円盤型であろうが、それだけを選択することはできないのです。なぜならそれは Canvas 上にビットマップで書かれた模様に過ぎないからです。

    ではどのようにしてアニメーションを実現するのかというと、Canvas 全体を描画してはクリアし描き換えていくという方法で実現します。

     

    フレームとアニメーション

    アニメーションの原理は、微妙に描写の異なる絵を高速に描き換えていくことで動画を実現しています。

    この描き換えられる絵のことをフレームといい、単位時間で描画されるフレームの数をフレームレートと言います。単位としては、1 秒あたりのフレームレートを fps (frames per second) という単位で表します。

    Canvas でアニメーションを実現するには、Canvas の描画とクリアを繰り返していく必要かありますが、ここで重要になってくるのが時間あたりの処理をさせるフレーム管理です。JavaScript には setInterval 関数があり、1 秒あたりの処理数を管理するのに以前から使用されてきました。しかし、HTML5 からは window オブジェクトによりアニメーションに向いた requestAnimationFrame メソッドが追加され、これを使用することができます。

    setInterval と requestAnimationFrame

    setInterval は単位時間当たりの処理を繰り返し行うための関数ですが、もともとがアニメーション用に作られているわけではないので、非効率な点がいろいろとありました。例えば、Web ブラウザーがバッググラウンドにあり、描画の必要がないときでも setInterval 関数は動作しつづけます。このためCPU サイクルの浪費、電力の無駄使いという問題が発生しました。また、モニターのリフレッシュレートとも合わせられず、過剰な描画呼び出しが発生し、バッテリの駆動時間や、他のアプリのパフォーマンスにも悪影響が及ぼす可能性がありました。

    いっぼう、requestAnimationFrame メソッドは、最初からアニメーションで使用されることが前提で作られているため、ブラウザーがページの表示を更新する必要のあるタイミングで (のみ) アプリは通知を受け取ることができるので、CPU やメモリを効率的に使用することができます。

     

    requestAnimationFrame メソッドの使い方

    requestAnimationFrame メソッドは、引数にフレームを描画するための関数をコールバック関数として渡し、継続して描画を行う際にはコールバック関数内でその関数をさらに呼ぶという使い方をします。

    例えは、以下は HTML エレメントの style.left を呼び出し毎に加算し、HTML エレメントを右方向へ動かす処理を記述したものですが、関数内の requestAnimationFrame メソッドに、呼び出し元と同じ関数名が指定されています。

    function renderLoop() {
             elm.style.left = (iPos += 3)  + "px";
             handle = window.requestAnimationFrame(renderLoop);
    }

     

    requestAnimationFrame メソッドと FPS

    requestAnimationFrame メソッドはブラウザーの負荷に合わせ、60 fps  以内で実行されます。実行は描画の準備が整った段階で行われるため、setInterval 関数を使ったアニメーションのような過剰な呼び出しによるコマ落ちや描画の乱れも発生せず、滑らかに動作します。

    ただし、1 秒間当たりの処理数は明示的に指定できないため、厳密な fps を設定するには独自でその仕組みを実装する必要があります。例えば、現在時刻から前回実行時刻をマイナスし、その差分で描画を行うかどうかという判断する、といった具合です。

    実際のところ、使用する Web ブラウザーごとに処理の発生するタイミングはまちまちなので、アニメーションさせるアイテムの数が増えてくるとその差が開き、Web ブラウザーごとに動作スピードが異なるということが発生します。(体験談)

    よって、fps を制御するコードを実装することをお勧めしますが、今回の実装では複雑になるので除きます。(※後の回で実装します。)

    requestAnimationFrame メソッドを使用したアニメーションの実装の詳細については以下のドキュメントを参照してください。

     

    requestAnimationFrame メソッドを使用した
    アニメーションの実装

    前置きが長くなりましたが前回まで作ったコードにアニメーションを実装し、雪の結晶の画像が繰り返し降ってくるようにします。

    原理としては、requestAnimationFrame メソッドに引数として渡される関数内で Canvas をクリアし、drawImage メソッドで画像を描画する際に縦の位置を表す引数 Y を加算していき、Y の値が Canvas の縦のサイズを超えたら 0 に戻すという単純なものです。

    それでは実際にコード書いていきましょう。

    まず、renderFrame という名前の新しい関数を定義します。Visual Studio を使用している方は func とタイプしてキーボードの [tab] キーを 2 回押下し、生成された myfunction 関数の名前を renderFrame に変更してください。

    renderFrame 関数内に Canvas をクリアするための context オブジェクトが持っている clearRect 関数を記述します。第一引数、第二引数は、クリアする領域の左上端の座標なので両方とも 0 を指定します。第三引数、第四引数は幅と高さを指定するので、canvas の width と height を指定します。

    //canvas をクリア
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    次に、雪の結晶の画像の現在の縦の座標を保持しているプロパティ _y を 2 つ増分し、画像を描画します。前の処理で Canvas 全体がクリアされているので雪だるまの画像も描画する必要があります。

    //雪の画像の縦位置を増分
    img_snow._y += 2;

    //画像を描画
    ctx.drawImage(img_snow, img_snow._x, img_snow._y);
    ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);

    最後に requestAnimationFrame メソッドの引数に renderFrame  関数を指定します。

    //ループを開始
    requestId = window.requestAnimationFrame(renderFrame);

    これで適切な場所から renderFrame  関数を呼び出すと、雪の結晶の画像が上からゆっくりと降りてきます。ただし、このままだと雪の結晶の画像の _y プロパティが加算され続けるため雪の結晶の画像は Canvas からはみ出して消えてしまいます。

    雪の結晶の画像が Canvas の表示領域をはみ出したら先頭に戻るように以下のコードを���数内の先頭に追加します。

    //img_snow の y 値(縦位置) が canvas からはみ出たら先頭に戻す
    if (img_snow._y > canvas.clientHeight) { img_snow._y = 0 };

    renderFrame 関数の全体のコードは以下のようになります。

    function renderFrame() {
        //img_snow の y 値(縦位置) が canvas からはみ出たら先頭に戻す
        if (img_snow._y > canvas.clientHeight) { img_snow._y = 0 };
        //canvas をクリア
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        //img_snow の y 値を増分
        img_snow._y += 2;
        //画像を描画
        ctx.drawImage(img_snow, img_snow._x, img_snow._y);
        ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
         //ループを開始
         requestId = window.requestAnimationFrame(renderFrame);
    }

    renderFrame 関数を呼び出しを記述します。

    renderFrame 関数を呼び出しは、Canvas への画像のロード処理と同様に画像が完全に読み込まれるのを待つ必要があるので、本来であればチェックルーチンを作って走らせる必要がありますが、まだ実装の初期段階なので今回は Canvas をクリックした際に呼びだすようにしましょう。

    loadAssets 関数の先頭部分で、変数 canvas に document.getElementById メソッドで HTML 上の Canvas のインスタンスを格納している箇所の直後に、以下のように addEventListener メソッドでイベントハンドラとして設定します。

    function loadAssets() {
           //HTML ファイル上の canvas エレメントのインスタンスを取得 
           canvas = document.getElementById('bg');
                   //アニメーションの開始
           canvas.addEventListener("click", renderFrame); 
     


    default.html を選択し、キーボードの [F5] キーを押下してページを実行し、画像が表示されたら Canvas 部分をクリックしてください。

    以下の画像のように雪の結晶画像が、繰り返し上から下へとアニメーションしていきます。

    image

    main.js ファイル全体の JavaScript コードは以下のようになります。

    (function () {
        //全体で使用する変数
        var canvas = null;
        var ctx = null;
        var img_snow = null;
        var img_snow_man = null;

        //DOM のロードが完了したら実行
        document.addEventListener("DOMContentLoaded", function () {
            loadAssets();
        });

        function loadAssets() {
            //HTML ファイル上の canvas エレメントのインスタンスを取得 
            canvas = document.getElementById('bg');
            //アニメーションの開始
            canvas.addEventListener("click", renderFrame);
            //2D コンテキストを取得
            ctx = canvas.getContext('2d');
            //image オブジェクトのインスタンスを生成
            img_snow = new Image();
            //image オブジェクトに画像をロード
            img_snow.src = '/img/snow.png';

            /*画像読み込み完了のイベントハンドラーに Canvas に
               画像を表示するメソッドを記述 */
            img_snow.onload = function () {
                img_snow._x = getCenterPostion(canvas.clientWidth, img_snow.width);
                img_snow._y = 0;
                //canvas 上で image を描画
                ctx.drawImage(img_snow, img_snow._x, img_snow._y);
            };
            //雪だるま画像のロード
            img_snow_man = new Image();
            img_snow_man.src = '/img/snow_man.png';
            img_snow_man.onload = function () {
                img_snow_man._x = getCenterPostion(canvas.clientWidth, img_snow_man.width);
                img_snow_man._y = canvas.clientHeight - img_snow_man.height;
                ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
            };
        };

        function renderFrame() {
            //img_snow の y 値(縦位置) が canvas からはみ出たら先頭に戻す
            if (img_snow._y > canvas.clientHeight) { img_snow._y = 0 };
            //canvas をクリア
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            //img_snow の y 値を増分
            img_snow._y += 2;
            //画像を描画
            ctx.drawImage(img_snow, img_snow._x, img_snow._y);
            ctx.drawImage(img_snow_man, img_snow_man._x, img_snow_man._y);
            //ループを開始
            requestId = window.requestAnimationFrame(renderFrame);
        }

        //中央に配置する画像の X 座標を求める関数
        function getCenterPostion(containerWidth, itemWidth) {
            return (containerWidth / 2) - (itemWidth / 2);
        };
    })();


    まとめ

    今回は Canvas を使用してアニメーションを実現する方法と、requestAnimationFrame メソッドを使用したアニメーションのの実装方法について紹介しました。

    次回はキーボードの矢印キーと画面のタッチで雪だるまを左右に動かせるように実装します。

     

    ⇒「HTML5 を使ったシンプルな 2 D ゲームの作り方 (矢印キーとタッチによる制御の実装)」へ

    Web Statistics
    Viewing all 51 articles
    Browse latest View live


    <script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>