自行撰寫ASP.NET MVC中路由(Routing)的條件約束

在ASP.NET MVC中我們可以針對 routes.MapRoute() 方法加上限制條件。

步驟1:新增一個Class,然後實作IRouteConstraint 介面,IRouteConstraint 介面只定義一個Match() 方法

YearMonthDayRouteConstraint.vb
Public Class YearRouteConstraint
    Implements IRouteConstraint

    ''' <summary>
    ''' 實作IRouteConstraint介面Match方法
    ''' </summary>
    ''' <param name="httpContext">包含有關個別 HTTP 要求的 HTTP 專屬資訊。</param>
    ''' <param name="route">提供用以定義路徑和取得路徑相關資訊的屬性及方法。</param>
    ''' <param name="parameterName">索引鍵/值組之不區分大小寫的集合</param>
    ''' <param name="values"></param>
    ''' <param name="routeDirection">表示 ASP.NET 路由是正在處理來自用戶端的 URL 還是正在產生 URL。</param>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Function Match(httpContext As System.Web.HttpContextBase,
                          route As System.Web.Routing.Route,
                          parameterName As String,
                          values As System.Web.Routing.RouteValueDictionary,
                          routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match

        ' IncomingRequest, 正在處理來自用戶端的 URL。
        ' InvariantCulture, 取得與文化特性無關的 (不變的) System.Globalization.CultureInfo。
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "year") Then
            Try
                Dim year As Integer = Convert.ToInt32(values("year"))
                If (year >= 1990) AndAlso (year <= 2100) Then
                    Return True
                Else
                    Return False
                End If
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function
End Class

Public Class MonthRouteConstraint
    Implements IRouteConstraint

    Public Function Match(httpContext As System.Web.HttpContextBase, route As System.Web.Routing.Route, parameterName As String, values As System.Web.Routing.RouteValueDictionary, routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "month") Then
            Try
                Dim month As Integer = Convert.ToInt32(values("month"))
                If (month >= 1) AndAlso (month <= 12) Then
                    Return True
                Else
                    Return False
                End If
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function

End Class

Public Class DayRouteConstraint
    Implements IRouteConstraint

    Public Function Match(httpContext As System.Web.HttpContextBase, route As System.Web.Routing.Route, parameterName As String, values As System.Web.Routing.RouteValueDictionary, routeDirection As System.Web.Routing.RouteDirection) As Boolean Implements System.Web.Routing.IRouteConstraint.Match
        If (routeDirection = Routing.RouteDirection.IncomingRequest) AndAlso
           (parameterName.ToLower(System.Globalization.CultureInfo.InvariantCulture) = "day") Then
            Try
                Dim month As Integer = Convert.ToInt32(values("month"))
                Dim day As Integer = Convert.ToInt32(values("day"))

                If (day < 1) Then
                    Return False
                End If

                Select Case month
                    Case 1, 3, 5, 7, 8, 10, 12
                        If (day <= 31) Then
                            Return True
                        End If
                    Case 2
                        If (day <= 28) Then
                            Return True
                        End If
                    Case 4, 6, 9, 11
                        If (day <= 30) Then
                            Return True
                        End If
                End Select
            Catch ex As Exception
                Return False
            End Try
        End If

        Return False
    End Function

End Class

YearMonthDayRouteConstraint.vb內含三個Class,都實作了IRouteConstraint 介面,程式很簡單,我們限制年( year )必須在1990 ~ 2100之間,月( month )必須在 1 ~ 12之間,日( day )有分大小月及二月的判斷。

步驟二:接下來我們在 Global.asax 加上一條路由規則,

routes.MapRoute("Archive",
                "Archive/{year}/{month}/{day}",
                New With {.contrller = "Archive", 
                          .action = "Index",
                          .year = UrlParameter.Optional,
                          .month = UrlParameter.Optional,
                          .day = UrlParameter.Optional},
                New With {.year = New YearRouteConstraint,
                          .month = New MonthRouteConstraint,
                          .day = New DayRouteConstraint})

重點在最後一個參數,我們傳入的路由規則的限制條件:

/Archive/2011/5/18 --> OK
/Archive/2011/2/29 --> Bad

此程式還有改進的空間,例如二月的潤月計算,會有29天,2012/2/29應該是要對的。除了寫Class之外,我們也可以直接使用RegEx 來設計限制條件,例如:

routes.MapRoute("Archive2",
                "Archive/{year}/{month}/{day}",
                New With {.contrller = "Archive",
                          .action = "Index",
                          .year = UrlParameter.Optional,
                          .month = UrlParameter.Optional,
                          .day = UrlParameter.Optional},
                New With {.year = "\d{4}",
                          .month = "\d{2}",
                          .day = "\d{2}"})

如果提供的是字串,路由會將字串視為規則運算式,並且呼叫 Regex 類別的 IsMatch 方法,藉此檢查參數值是否有效。這邊我們規則很簡單,year只要是4位數數字,month及day只要是2位數數字。

以上我們簡單示範如何幫路由設計、撰寫及使用限制條件,這不只是限制,也是安全性的提升。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。