2011年1月14日金曜日

WebMatrixの認証設定とヘルパー

WebMatrixを使ってWebPagesの認証機能を利用する場合、WebSecurityヘルパーを使う事になりますね。詳しくは塚田さんがエントリ書いてたのでそちらをどうぞ。

WebMatrix ASP.Net Razorでログイン認証を作る- 1.WebSecurity Helperの概要 « IIS+αでいってみよう

↑ここから何個か続きますね。

基本的にASP.NETの仕組みで認証(Authentication)・承認(Authorization)させてるのでFormsAuthentication周りを知っていることが前提なのかもしれないけど、その辺はヘルパーを使うということでまるっと隠しきろうという感じでしょうね。

WebMatrix入れるとスターターサイトを作れますが、そこにも認証系の機能はひと通り含まれてます。それを参考にするのがいいかなと思うんだけど、1点とても分かりにくいところがある。

それは「ログインページを指定」する方法です。

WebSecurityの規定値は”~/Account/Login”です。これはFormsAuthenticationSettingsクラスにstatic readonlyで宣言されてます。readonlyですよ!変更できないじゃん!

普通にASP.NETの作法通りなら以下の設定をしますよね。
<system.web>
<authentication>
  <forms loginUrl=”…”>

でもWebSecurityはそれを利用しません。さすがカスタム。あと<authorization>設定(承認)は設定すればちゃんと機能します(でも使わないのがWebMatrix流?)。

FormsAuthenticationModule自体は読み込まれてるので、web.configを作成してそれらのセキュションを書いてしまえばよさそうな気がするでしょ?だがしかし!loginUrlは見てくれないんですよ。なぜかというとアプリケーション起動時にFormsAuthenticationの設定をWebMatrix.WebData.ConfigUtilが上書きするからです。

なので、ログインURLを自分で指定したい場合、ソースを追いかけるとわかりますが、以下のようにappSettingsに指定しましょう。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="loginUrl" value="~/Members/LogOn"/>
  </appSettings>
</configuration>

これで、標準のログイン先が変更できます。
なんで、こういう設定が必要かというとHttpのステータスコードを見てFormsAuthenticationModuleがリダイレクトするのがASP.NETのForm認証フローだからです。これを理解してないと自分でSessionやらCookieやらで適当につくろうとしちゃうでしょ?それはとてもよろしくないです。愚行ですよ!

なので、authorizationで認証が必要なパスを指定するか、認証が必要なすべてのページの最初にWebSecurity.RequireAuthenticationUser()を書いておくことです(実際は_PageStartとか_Layoutとかで一括処理ね)。そうするとHttpUnauthorized 401なレスポンスを返そうとするので、FormsAuthenticationModuleがそこに反応してログインページに自動でリダイレクトします。

分かりにくい?

@{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "マイ Web サイトへようこそ";
}
<p>
    ASP.NET Web Pages を使用すると、Web 用の優れた .NET ベースのアプリケーションを簡単に作成できます。
</p>
<p>
    <ul>
    @foreach(var moduleName in Context.ApplicationInstance.Modules.AllKeys){
        <li>@moduleName : (@Context.ApplicationInstance.Modules[moduleName].GetType())</li>
    }
    </ul>
    認証済み:@WebSecurity.IsAuthenticated<br/>
    ログインURL:@FormsAuthentication.LoginUrl
</p>


↑こんな感じでdefault.cshtmlに書いておくと読み込まれてるすべてのモジュールと認証状況を表示します。web.configに何も書かない場合、ログインURLには"/Account/Login"と表示されますですよ。

auth1

↑一番下の部分ね。

続いてweb.configファイルをルートに作成し設定してみましょう。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="loginUrl" value="~/Members/LogOn"/>
  </appSettings>
</configuration>

auth2

ちゃんとログインURLが変化してますよね。この状態でログイン必須なChangePasswordページに行ってみる。

auth3

"/Members/LogOn”というページはないので、404エラーになってますが、ちゃんと"/Members/LogOn”にリダイレクトされてるのが確認できます。ぐっじょぶ!
難しい話はこのへんにして、今日のヘルパー。

@helper Show(string message){
        <script type="text/javascript">
            alert('@Html.Raw(message.Replace("'","\\'"))');
        </script>
    }
}
/App_CodeにMessageBox.cshtmlを作成し上記のコードをコピペ。
Default.cshtmlに以下のように書いてみよう。
@{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "マイ Web サイトへようこそ";
}
<p>
    ASP.NET Web Pages を使用すると、Web 用の優れた .NET ベースのアプリケーションを簡単に作成できます。
</p>
<p>
    @MessageBox.Show("Hello World! '10")
    @MessageBox.Show("Hello World!")
</p>

あら不思議!サーバーサイドでMessageBox.Showを書いたつもりがクライアントサイドにメッセージボックスが表示されました。デバッガのないWebMatrixならではのちゃらいヘルパーの完成です。

auth4

IIS ExpressのVS2008/VS2010利用

IIS ExpressをVisual Studio 2010に統合するにはSP1を待たないとだめですよね。でも、そんなの待ちきれない!という人はたくさんいることでしょう。IIS Expressをコマンドラインから起動パラメータを確認してみましょう。

iisex1

c:\Program files(x86)\IIS Express\iisexpress /?

IIS Express :
------------------
iisexpress [/config:config-file] [/site:site-name] [/siteid:site-id] [/systray:true|false] [/trace:trace-level]
iisexpress /path:app-path [/port:port-number] [/clr:clr-version] [/systray:true|false] [/trace:trace-level]

/config:config-file
applicationhost.config  Documents  IISExpress\config\applicationhost.config

/site:site-name
applicationhost.config

/siteid:site-id
applicationhost.config  ID

/path:app-path
/config

/port:port-number
8080 /path

/clr:clr-version
.NET Framework  (: v2.0) v4.0 /path

/systray:true|false
   true

/trace:trace-level
'none''n''info''i''warning''w''error' 'e'

\:
iisexpress /site:WebSite1
  WebSite1

iisexpress /config:c:\myconfig\applicationhost.config


iisexpress /path:c:\myapp\ /port:80
'80  'c:\myapp'

いろいろあるけど、プロジェクトファイルのパスと実行時のポートを指定すれば概ね動くってことなので、プロジェクトのプロパティを以下のように指定しましょう。

iisex2

動作開始のところで「外部プログラムを起動する」を選択して
C:\Program Files (x86)\IIS Express\iisexpress.exe
を指定。コマンドライン引数に
/path:プロジェクトフォルダ /port:8080
を指定(ポートはかぶらなければ何でも良し)。

あとはいつもどおり実行(F5)すると、コンソールが立ち上がってIIS Express経由でWebアプリケーションが起動します。とりあえずはブラウザの自動起動はしないので、ブラウザを立ち上げて
http://localhost:8080
と、入れて自分で開きましょう。この状態でもデバッグできるのでとてもゴキゲンです!

iisex3

iisex4

しかも同じ方法でVisual Studio 2008からも使えるので超絶便利!VS2008からは/clr:v2.0でランタイムバージョン指定も忘れずに。なんせ本番環境と同一のIISでデバッグできるし並列でリクエスト処理してくれてチョッパヤだし!Cassiniさらば!

WebMatrixで初めてのヘルパー

ASP.NET MVC3 RTM出ましたね。やったね。やりたい事いろいろあるんだ!

WebMatrixもでたね。WebPagesじゃん?Razorじゃん?ヘルパーじゃん?

WebMatrixやらなんやらの細かい説明とかはよくわからないのですっ飛ばして、早速ヘルパーを作ってみようかな。

ヘルパーといえばHelperResultを受け取る@helperで定義される者たちなんだろうけど、@functionsに定義するものもUtilityメソッドという意味でヘルパーっていう扱いにしても、それはそれで許されるんじゃないかと勝手に定義。だめ~?いいっしょ!

Webアプリケーションの基本といえばキャッシュでしょ!でもとりあえず今回はOutputCacheじゃなくてデータキャッシュね。System.Runtime.Caching!と言いたいところだけど、もっとお手軽なHttpContextBase.Cache。いろいろまとめてくれてるし、System.Web.Caching.Cache互換なインターフェイスだし。

まずは、WebMatrixでスターターサイトを作成。

ルートのdefault.cshtmlをひらいて以下のように書いてみる。

@{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "マイ Web サイトへようこそ";

    dynamic cache = Cache["test_page"];
    if(cache==null){
        cache = DateTime.Now;
        Cache.Insert(
            "test_page",
            cache,
            null,
            DateTime.Now.AddSeconds(3),
            System.Web.Caching.Cache.NoSlidingExpiration
        );
    }
}
<p>
    ASP.NET Web Pages を使用すると、Web 用の優れた .NET ベースのアプリケーションを簡単に作成できます。
</p>
<p>
    @cache<br/>
    @cache.GetType().Name
</p>

んで、動かしてみる。

helper1

出ますわね。3秒キャッシュ。ホームをなんどもクリックしてみたら分かるわね。

毎回キャッシュするたびにこんなコード書くのも面倒だよね!これをヘルパーにしてみよう!

ルートにApp_Codeって名前でフォルダ作成。んで、その中にUtility.cshtmlファイルを作成。

helper2

中身は↓。

@functions {
    public static new dynamic Cache(HttpContextBase context, string key, int expireSeconds, Func<dynamic> thunk){
        dynamic value = context.Cache[key];
        if(value == null) {
            value = thunk();
            context.Cache.Insert(
                key,
                value,
                null,
                DateTime.Now.AddSeconds(expireSeconds),
                System.Web.Caching.Cache.NoSlidingExpiration
            );
        }
        
        return value;
    }
}

staticメソッドなのでHelperPage.Cacheなんて見れないからContextを渡しましょう。もちろんHelper内で

var context = new HttpContextWrapper(HttpContext.Current);

って書くのもありだとは思うけど、まぁ、ね。

んで、default.cshtmlを以下のように変更。

@{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "マイ Web サイトへようこそ";

    dynamic cache = Cache["test_page"];
    if(cache==null){
        cache = DateTime.Now;
        Cache.Insert(
            "test_page",
            cache,
            null,
            DateTime.Now.AddSeconds(3),
            System.Web.Caching.Cache.NoSlidingExpiration
        );
    }
}
<p>
    ASP.NET Web Pages を使用すると、Web 用の優れた .NET ベースのアプリケーションを簡単に作成できます。
</p>
<p>
    @Utility.Cache(Context, "test_helper",3,()=>DateTime.Now)<br/>
    @Utility.Cache(Context, "test_helper",3,()=>DateTime.Now).GetType().Name<br/>

    @cache<br/>
    @cache.GetType().Name
</p>

太字の部分追加。

キャッシュしたい値を返すFunc<T>を渡しましょう。

@Utility.Cache(Context,”text_helper”,3,()=>{
  return DateTime.Now
})

でもいいよ。真面目な話をすると、ちゃんとレキシカルスコープを維持してるので、ローカル変数を参照しててもOKです。...。どんまい。

helper3

ちゃんと3秒キャッシュしてるよね!例えば、外部のサイトのデータを毎回取得するなんてばからしいからキャッシュしたり、データベースから毎回取得するなんてばからしいからキャッシュしたり、いろいろキャッシュすることでパフォーマンスは劇的にあがるっしょ!

dynamicだから

@Utility.Cache(Context, "test_helper",3,()=>DateTime.Now).Second

って書いてもちゃんと動くもんね!

まずは1 helper。

2026年06月09日の記事一覧

(全 21 件) Classic Pro プレミアム4芯マイクケーブルレビュー|NEUTRIK REANコネクタで確実な接続&ノイズにも強い | ギターいじリストのおうち RØDE ソナウラ | RØDE (JP) 「AIバブルは2030年に崩壊する」—...