iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 26
2
Software Development

從Asp.net框架角度進入Asp.net MVC原始碼系列 第 26

[Day26] 動手DIY改造 Asp.net MVC- Route解析機制

前言

UrlRoutingModule對於OnPostResolveRequestCache事件添加一個對於MVC很重要的動作,透過RouteCollection取得此次請求匹配RouteData物件.

利用此RouteData取得要使用的IHttpHandler來執行它.

RouteData routeData = RouteCollection.GetRouteData(context);

RouteCollection是全域路由註冊表.我們在一開始使用MapRoute註冊與之匹配ControllerAction

RouteCollection是基於RouteBase物件集合,所以它可以存放所有繼承RouteBase物件,RouteBase這個類別有一個重要的方法來取得RouteData,RouteData封裝此次Http請求的Controller,Action...等資訊

對於每個Http請求依序找尋第一個匹配路由規則

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

RouteData

RouteData類別中有幾個重要屬性.

  • RouteHandler:存放IRouteHandler物件(提供IHttpHander並呼叫執行物件)
  • Values: 一個字典集合,存放Key為ControllerAction,ValueURL參數值相對位置參數
  • GetRequiredString:利用傳入string參數對於Values字典取匹配名稱.
public class RouteData
{
    private RouteValueDictionary _values = new RouteValueDictionary();
    private RouteValueDictionary _dataTokens = new RouteValueDictionary();
    private IRouteHandler _routeHandler;

    /// <summary>
    ///   使用指定的路由及路由處理常式,初始化 <see cref="T:System.Web.Routing.RouteData" /> 類別的新執行個體。
    /// </summary>
    /// <param name="route">此物件會定義路由。</param>
    /// <param name="routeHandler">處理要求的物件。</param>
    public RouteData(RouteBase route, IRouteHandler routeHandler)
    {
      this.Route = route;
      this.RouteHandler = routeHandler;
    }

    /// <summary>
    ///   取得自訂值集合,當 ASP.NET 路由判斷路由是否符合要求時,會將這些值傳遞至路由處理常式但不會使用。
    /// </summary>
    public RouteValueDictionary DataTokens
    {
      get
      {
        return this._dataTokens;
      }
    }

    /// <summary>取得或設定代表路由的物件。</summary>
    public RouteBase Route { get; set; }

    /// <summary>取得或設定處理要求路由的物件。</summary>
    public IRouteHandler RouteHandler
    {
      get
      {
        return this._routeHandler;
      }
      set
      {
        this._routeHandler = value;
      }
    }

    /// <summary>取得 URL 參數值和預設路由值的集合。</summary>
    public RouteValueDictionary Values
    {
      get
      {
        return this._values;
      }
    }

    /// <summary>擷取具有指定識別項的值。</summary>
    public string GetRequiredString(string valueName)
    {
      object obj;
      if (this.Values.TryGetValue(valueName, out obj))
      {
        string str = obj as string;
        if (!string.IsNullOrEmpty(str))
          return str;
      }
      throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteData_RequiredValue"), new object[1]
      {
        (object) valueName
      }));
    }
}

RouteData主要把Client傳送Http請求資訊經解析後存放在Values中.

RouteBase中有個GetRouteData方法,藉由我們的路由設定去解析當前是否匹配到路由規則,如果有就回傳一個RouteData物件,否則回傳Null

建立自己Route機制

一般使用Route這個物件是使用/當作註冊對應的規則

{Controller}/{Action}Domian後用/當作分隔

第一個區塊字串被當作ControllerName

第二個區塊字串被當作ActionName

因為在Asp.net MVC透過RouteData.GetRequiredString傳入ControllerNameActionName取得相對應的值.

這次例子我們希望可以透過QueryString來製作Route對應規則

{domain}?controller=home&action=about

透過上面URL期望呼叫HomeController.About方法

廢話不多說我們來看一下這個QueryStringRoute是如何被實現

public class QueryStringRoute : RouteBase
{
    public string Url { get; set; }

    private bool Match(NameValueCollection queryString, out IDictionary<string, string> variables)
    {
        variables = new Dictionary<string, string>();

        var para = Url.Split('&');
        if (!para.All(x=>queryString.AllKeys.Contains(x)))
            return false;

        variables = para.ToDictionary(x => x, y => queryString[y]);

        return true;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        IDictionary<string, string> value;

        if (Match(httpContext.Request.QueryString,out value))
        {
            RouteData routeData = new RouteData(this, new MvcRouteHandler());

            foreach (var dict in value)
                routeData.Values.Add(dict.Key,dict.Value);

            return routeData;
        }

        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        return null;
    }
}

我們實現RouteBase抽象類別兩個方法

  • GetRouteData
  • GetVirtualPath

其中GetRouteData是我們主要要實作方法

Request.QueryString這個集合封裝Http QueryString的資訊.

首先我們先判斷此次請求QueryString是否由傳Controller,Action資料過來,如果有把值填入RouteData.Values字典集合中,反之不匹配此Route規則就回傳NULL.

MVCRouteData.Values取得對應的資料.

使用上就可透過RouteCollection.AddRoute添加到集合中

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add("customer",new QueryStringRoute()
    {
        Url = "controller&action"
    });
}

Http請求就會依序找尋第一個匹配Route來執行.

小結:

透過繼承RouteBase抽象類別並實現GetRouteData方法透過返回RouteData物件對於Http請求資訊封裝到RouteData.Values字典集合.(在MVC框架中會對於Values字典中取KeyControllerAction的值.)

最後再把新建立RouteBase物件加入到全域RouteCollection中.

希望大家看完這篇後可以了解並自行擴充自己Route機制.

本次範例程式碼Git Sample(CustomerRoute Branch)


上一篇
[Day25] 動態產生程式碼(WebViewPage) View是如何被建立(四)
下一篇
[Day27] 動手DIY改造 Asp.net MVC- DependencyResolver by Autofac
系列文
從Asp.net框架角度進入Asp.net MVC原始碼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言