文章出處

問題

怎么樣限制路由中參數的值。

 

解決方案

ASP.NET WEB API 允許我們通過 IHttpRouteConstraint 接口設置路由約束。集中式路由和直接式路由都可以使用 IHttpRouteConstraint。

 

框架提供了 18 個接口,他提供了大部分類型的約束,例如,路由參數長度相關的約束,可以確保值都在定義范圍內,或者限制數據類型。當然也可以通過實現接口 IHttpRouteConstraint 來自定義約束邏輯。

 

工作原理

IHttpRoutConstraint 是一個 HTTP 路由約束接口(如代碼片段 3-11),并公開了一個簡單的方法 Match,這個方法需要五個參數,HttpRequestMessage 實例,IHttpRoute 實例,string 類型的 parameterName,Idictionary<string,object> 類型的路由 value,HttpRouteDirection 類型的 routeDirection,也是為了保證路由可以基于應用程序的邏輯被匹配到。

 

代碼片段 3-11 IHttpRouteConstraint 定義

public interface IHttpRouteConstraint
{
    bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
        IDictionary<string, object> values, HttpRouteDirection routeDirection);
}

 

也可以通過使用 CompoundRouteConstraint 進行復合約束,需要通過構造函數添加到 IHttpRouteConstraint 集合中,如表格 3-1 所示,展示內建的約束和基本用法

 

表格 3-1. ASP.NET WEB API 中 IHttpRouteConstraint 可以用的約束

 

 

屬性路由,是通過 DefaultInlineConstraintResolver 來映射嵌入約束的版本和實際類型。當調用 MapHttpAtrributeRoute 的時候,ASP.NET WEB API 會使用解析器轉換嵌入約束為相關 IHttpRouteConstraint 實例。為了采用某些約束處理自定義約束,也可以修改 DefaultInlineConstraintResolver 或者自己實現全部的 IInineConstraintReslover 接口。不管怎么樣,都需要傳一個他的實例給方法 MapHttpAttributeRoute。 

 

OptionalRouteConstraint 是被用來提供可選參數功能的,如上一篇 3-4 介紹的,還提供了常見的約束功能。如果路由參數不是 RouteParameter.Optional 的,OptionalRouteConstraint 就只會計算約束。

 

代碼演示

對于集中式路由,約束是作為 MapHttpRoute 方法的第三個參數傳進來的。與默認值類似,已經在 3-3 部分介紹過,這個參數的類型是 Idictionary<string,object>,但是,框架的設計也是可以傳匿名對象,這個方法簽名的實際類型就是一個簡單對象。約束參數的名稱必須和路由模板以及 Action 的簽名一致。

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "orders/{text}",
    constraints: new {text = new AlphaRouteConstraint()},
    defaults: null
    );

 

使用集中式路由,也可以定義一個字符串的嵌入正則表達式,不必使用任何 IHttpRouteConstarint 接口。在下面的李子中,“id”就是一個數字約束嵌入正則表達式。

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "size/{id}",
    constraints: new {id = "\d+"},
    defaults: null
    );

 

集中式路由也可以約束 HTTP 方法,只要通過一個預定義的 httpMethod 鍵并賦值 HttpMethodConstrtin 就可以。

 

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "size/{id}",
    constraints: new {httpMethod = new HttpMethodConstraint(HttpMethod.Get)},
    defaults: null
    );

 

對于直接式路由,可以通過在參數加上冒號再加上約束條件。

[Route("orders/{text:alpha}")]
public HttpResponseMessage Get(string text){}

 

對于復合路由的定義,集中式路由需要通過 CompoundRouteConstraint 轉換。

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "orders/{text}",
    constraints: new
    {
        text = new CompoundRouteConstraint(new List<IHttpRouteConstraint>
        {new AlphaRouteConstraint(), new MaxLengthRouteConstraint(5)}
    },
    defaults: null
    );

 

對于屬性路由,可以通過冒號將不同的約束鏈起來;框架會在內部使用 CompoundRouteConstraint 構建復合約束。 

[Route("orders/{text:alpha:maxlength(5)}")]
public HttpResponseMessage Get(string text){}

 

一個簡單的自定義路由約束,確保參數是一個合法的 email 格式,如代碼片段 3-12 所示。因為路由值是 IDictionary<string.object>,再驗證約束之前需要轉換成期望的類型(這里就是 string)。

 

public class EmailRouteConstraint : IHttpRouteConstraint
{
    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
    IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            var stringValue = value as string;
            if (stringValue == null) return false;
            try
            {
                var email = new MailAddress(stringValue);
                return true;
            }
            catch (FormatException)
            {
                return false;
            }
        }
        return false;
    }
}

 

直接在集中式路由中,像使用內置約束一樣使用這個約束。 

config.Routes.MapHttpRoute(
    name: "Email",
    routeTemplate: "{controller}/email/{text}",
    constraints: new {text = new EmailRouteConstraint()},
    defaults: null
    );

 

 

然而,使用屬性路由的時候,可以使用別名來代替,但是在 EmailRouteConstraint 類沒有這樣的定義。因為,別名和約束類型是通過 DefaultInlineConstraintReslover 來完成映射的,ASP.NET WEB API 使用這個來解析這個約束,我們需要執行如下步驟

 

var constraintResolver = new DefaultInlineConstraintResolver();
constraintResolver.ConstraintMap.Add("email", typeof (EmailRouteConstraint));
config.MapHttpAttributeRoutes(constraintResolver);

 

這樣,就可以像使用框架提供的約束一樣,使用上面定義的 email 約束。

 

[Route("orders/client/{text:email}")]
public HttpResponseMessage GetByClient(string text) { }

 

注意 如果沒有額外的映射步驟,約束就不會有任何反應,但是,ASP.NET WEB API 的整個屬性路由可能有問題。

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()