カテゴリー別アーカイブ: Filter

【F# + ASP.NET】 承認フィルターによるHTTPS通信に制限するには

◆ Controller

namespace FsWeb.Controllers

open System.Web
open System.Web.Mvc

// HTTPSに制限する承認フィルター
// ※ Visual StudioでSSLを有効にするには、Webプロジェクトのプロパティで、
//     [SSL有効化]にするを "True" に設定。 
[<RequireHttps>]
type HomeController() =
    inherit Controller()
    member this.Index () =
        this.View() :> ActionResult

広告

【F# + ASP.NET MVC】 結果フィルターによるキャッシュ機能を有効にするには

◆ Controller

namespace Sample.Controllers

open System
open System.Web
open System.Web.Mvc

type HomeController() =
    inherit Controller()
    
    // キャッシュ機能により、10秒ごとにしか更新されない
    [<OutputCache(Duration=10, VaryByParam="*")>]
    member this.Index() =
        // キャッシュ テスト
        this.ViewData.["Message"] <- System.DateTime.Now
        this.View() :> ActionResult

◆ View

@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div>
        @ViewData["Message"]
    </div>
</body>
</html>

【F# + ASP.NET MVC】 アクション フィルターによるロギングを行うには

◆ アクション フィルター

namespace Sample.Attributes

open System
open System.Diagnostics
open System.Web.Mvc
open System.Web.Routing

type LogActionFilterAttribute() = 
    inherit ActionFilterAttribute()

    // ログ出力
    member private x.PutLog(methodName : string, routeData : RouteData) =
        let controllerName = routeData.Values.["controller"]
        let actionName = routeData.Values.["action"]
        let message = String.Format("{0} controller:{1} action:{2}"
            , methodName, controllerName, actionName)
        Trace.WriteLine(message, "アクション フィルター")

    override x.OnActionExecuting(filterContext) =
        x.PutLog("OnActionExecuting", filterContext.RouteData)

    override x.OnActionExecuted(filterContext) =
        x.PutLog("OnActionExecuted", filterContext.RouteData)

    override x.OnResultExecuting(filterContext) =
        x.PutLog("OnResultExecuting", filterContext.RouteData)

    override x.OnResultExecuted(filterContext) =
        x.PutLog("OnResultExecuted", filterContext.RouteData)

◆ Global.fs

namespace Sample.Routing

open System
open System.Web
open System.Web.Mvc
open System.Web.Routing
open System.Reflection

open Sample.Mef
open Sample.Controllers
open Sample.Attributes

type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    // フィルターを登録
    static member RegisterGlobalFilters(filters:GlobalFilterCollection) =
        // アクション フィルターをすべてのコントローラへ適用する
        filters.Add(new LogActionFilterAttribute())

    static member RegisterRoutes(routes:RouteCollection) =
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )

    member this.Start() =
        AreaRegistration.RegisterAllAreas() 
        
        // フィルターを登録
        Global.RegisterGlobalFilters(GlobalFilters.Filters)
        Global.RegisterRoutes(RouteTable.Routes) |> ignore

◆ Controller

namespace Sample.Controllers

open System.Web
open System.Web.Mvc
open System.Diagnostics

open Sample.Attributes

type HomeController() =
    inherit Controller()
    
    member this.Index() =
        Trace.WriteLine("アクション実行")
        this.View() :> ActionResult

◆ 実行結果

アクション フィルター: OnActionExecuting controller:Home action:Index
アクション実行
アクション フィルター: OnActionExecuted controller:Home action:Index
アクション フィルター: OnResultExecuting controller:Home action:Index
アクション フィルター: OnResultExecuted controller:Home action:Index

【F# + ASP.NET MVC】 承認フィルターによるログイン認証を行うには

◆ ログイン認証プロバイダー

namespace Sample.Authorize

// #r "System.Web.ApplicationServices"
// #r "System.Configuration"
open System
open System.Web.Security

type CustomMembershipProvider() = 
    inherit MembershipProvider()

    override val Name = "customMembershipProvider" with get

    override x.ValidateUser(username : string, password : string) =
        // ユーザー名とパスワードが一致したら認証OKとする。
        username.Equals(password)

    override x.GetPassword(username, answer) =
        raise <| new NotImplementedException()
    
    // 省略(以下 override は、同様に NotImplementedException を実装
    // ・・・

◆ ロール プロバイダー

namespace Sample.Authorize

// #r "System.Web.ApplicationServices"
// #r "System.Configuration"
open System
open System.Web.Security

type CustomRoleProvider() = 
    inherit RoleProvider()

    override val Name = "customRoleProvider" with get

    override x.GetRolesForUser(username) =
        // ユーザー名が admin であれば管理者とする。
        if username.Equals("admin") then
            [|"Administrators"|]
        else
            [|"Gests"|]

    override x.GetUsersInRole(roleName) =
        raise <| new NotImplementedException()

    // 省略(以下 override は、同様に NotImplementedException を実装
    // ・・・

◆ Web.config

<?xml version="1.0"?>
<configuration>
  <!-- 省略 -->
  <system.web>
    <!-- フォーム認証(認証エラー時の遷移先) -->
    <authentication mode="Forms">
      <forms loginUrl="~/Login/Index" timeout="2880" />      
    </authentication>
    <!-- ログイン認証プロバイダー -->
    <membership defaultProvider="customMembershipProvider">
      <providers>
        <clear/>
        <add name="customMembershipProvider"
             type="Sample.Authorize.CustomMembershipProvider" />
      </providers>
    </membership>
    <!-- ロール プロバイダー -->
    <roleManager enabled="true" defaultProvider="customRoleProvider">
      <providers>
        <clear/>
        <add name="customRoleProvider"
             type="Sample.Authorize.CustomRoleProvider" />
      </providers>
    </roleManager>
  </system.web>
  <!-- 省略 -->
</configuration>

◆ Entity

namespace Sample.Entities

type Employee() = 
    member val Username = "" with get, set
    member val Password = "" with get, set

◆ ログイン用Controller

namespace Sample.Controllers

open System
open System.Web.Mvc
open System.Web.Security

open Sample.Entities
open Sample.Authorize

type LoginController() =
    inherit Controller()

    // ログイン認証プロバイダー
    member val Provider = new CustomMembershipProvider() with get, set
    
    member this.Index() =
        FormsAuthentication.SignOut()
        this.View() :> ActionResult

    [<HttpPost>]
    member this.Index(entity:Employee) =
        // ログイン認証
        if this.Provider.ValidateUser(entity.Username, entity.Password) then
            // 認証OK
            // 認証情報をURLへ保持
            FormsAuthentication.SetAuthCookie(entity.Username, false)
            // HOME画面へ遷移
            base.RedirectToAction("Index", "Home") :> ActionResult
        else
            // 認証NG
            this.ViewData.["Message"] <- "認証NG"             
            this.View(entity) :> ActionResult

◆ ログイン用View

@model Sample.Entities.Employee

@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <title>F# + ASP.NET</title>
</head>
<body>
    <div>
        @using (Html.BeginForm()) {
            <fieldset>
                <legend>User</legend>
                <div class="editor-label">
                    @Html.LabelFor(model => model.Username)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.Username)
                </div>
                <div class="editor-label">
                    @Html.LabelFor(model => model.Password)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.Password)
                </div>
                <p>
                    <input type="submit" value="Login" />
                </p>
            </fieldset>
        }
    </div>
    <div>
        @ViewData["Message"]
    </div>
</body>
</html>

◆承認フィルターを実装したController(HomeController)

namespace Sample.Controllers

open System.Web
open System.Web.Mvc

type HomeController() =
    inherit Controller()
    
    // 認証フィルター(ロールが administrators の場合のみアクション可能)
    [<Authorize(Roles = "administrators")>] 
    member this.Index() =
        this.ViewData.["Message"] <- "Hello World!"
        this.View() :> ActionResult

◆認証時に表示するView(HomeView)

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div>
        @ViewData["Message"]
    </div>
</body>
</html>

【F# + ASP.NET MVC】 例外フィルターによるエラーログを出力するには

◆ 例外フィルター

namespace Sample.Attributes

open System;
open System.Web;
open System.Web.Mvc;
open System.Diagnostics

type ErrorLogAttribute() =
  inherit FilterAttribute()
  // IExceptionFilter インターフェースを実装
  interface IExceptionFilter with
    // アクション・メソッドで例外が発生した場合に実行
    member this.OnException(filterContext : ExceptionContext) =
      if box(filterContext) = null then
        raise <| new ArgumentNullException("NoContextException")
      else
        // 例外が HttpException の場合、ログ出力をせず、
        // 指定されたレスポンスコードとエラーメッセージを返す
        if filterContext.Exception.GetType() = typeof<HttpException> then
          // 例外を処理済みとみなす
          filterContext.ExceptionHandled <- true
          // HTTP Status code を設定
          filterContext.HttpContext.Response.StatusCode <-
            (filterContext.Exception :?> HttpException).GetHttpCode()
          // エラーView(Shared\CustomError.cshtml)を表示
          filterContext.Result <- 
            new ViewResult(ViewName = "CustomError", 
              ViewData = new ViewDataDictionary(filterContext.Exception))
        else
          // エラーログ出力
          // 今回はトレースログに出力とする
          let route = filterContext.RouteData
          Trace.WriteLine(filterContext.HttpContext.Request.RawUrl)
          Trace.WriteLine(route.Values.Item("controller").ToString)
          Trace.WriteLine(route.Values.Item("action").ToString)
          Trace.WriteLine(filterContext.Exception.StackTrace)
          Trace.WriteLine(DateTime.Now)

          // 例外を処理済みとみなす
          filterContext.ExceptionHandled <- true
          // HTTP Status code を設定
          filterContext.HttpContext.Response.StatusCode <- 500;
          // エラーView(Shared\CustomError.cshtml)を表示
          filterContext.Result <- 
            new ViewResult(ViewName = "CustomError")

◆ Global.fs

namespace Sample.Routing

open System
open System.Web
open System.Web.Mvc
open System.Web.Routing
open System.Reflection

open Sample.Mef
open Sample.Controllers
open Sample.Attributes

type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    // フィルターを登録
    static member RegisterGlobalFilters(filters:GlobalFilterCollection) =
        filters.Add(new HandleErrorAttribute())
        // ErrorLog フィルターをすべてのコントローラーへ適用する
        filters.Add(new ErrorLogAttribute())

    static member RegisterRoutes(routes:RouteCollection) =
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )

    member this.Start() =
        AreaRegistration.RegisterAllAreas() 
        
        // フィルターを登録
        Global.RegisterGlobalFilters(GlobalFilters.Filters)
        Global.RegisterRoutes(RouteTable.Routes) |> ignore
        

◆ Controller

namespace Sample.Controllers

open System
open System.Web
open System.Web.Mvc
open Sample.Attributes
open System.Diagnostics

type ErrorController() =
    inherit Controller()
    
    member this.Index() =
        // 例外を発生させる(テスト)
        raise <| new Exception()
        this.View() :> ActionResult

◆ View(エラー発生時に表示)

// ..\Views\Shared\CustomError.cshtml
<!DOCTYPE html>
<html>
<head>
    <title>カスタムエラーページ</title>
</head>
<body>
    <div>
      予期せぬエラーが発生しました。
    </div>
</body>
</html>