動手DIY改造 Asp.net MVC- Route解析機制 (第26天)

Agenda

前言

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

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

1
RouteData routeData = RouteCollection.GetRouteData(context);

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

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

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

1
2
3
4
5
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字典取匹配名稱.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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是如何被實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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添加到集合中

1
2
3
4
5
6
7
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)

此文作者:Daniel Shih(石頭)
此文地址https://isdaniel.github.io/ithelp-day26/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!


如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^