Die benutzerdefinierten Routen werden mithilfe von routes.add in der Methode "RegisterRoutes" für die RouteConfig-Klasse definiert.Wie Routentabelle erstellt wird, wenn benutzerdefinierte Route in MVC definiert ist
routes.Add("Default", new ABCRoute("{*url}",
new
{
languageCulture = "en",
pos = "uk",
area = defArea,
controller = "XXX",
action = "YYY",
id = UrlParameter.Optional
},
new { languageCulture = @Supportedlang , pos = @SupportedPos },
null, new MvcRouteHandler()));
Die ABCRoute ist eine Klasse, die die Route-Klasse erbt.
Die ABCClass ähnelt GreedyClass im folgenden Link und wird unten gezeigt.
on Erste Anfrage an die Anwendung, Dieser Konstruktor mit Parameter wird aufgerufen, und es gibt nichts zurück.
Wie die Route definiert und die Routentabelle erstellt wird, wenn wir die Route.Add() - Methode wie oben angegeben aufrufen.
Wie Routentabelle hat alle mögliche Kombination, da Route trotzte ist dynamisch nicht statisch.
1: using System.Collections.Generic;
2: using System.Globalization;
3: using System.Linq;
4: using System.Text;
5: using System.Text.RegularExpressions;
6:
7: namespace System.Web.Routing
8: {
9: /// <summary>
10: /// This route is used for cases where we want greedy route segments anywhere in the route URL definition
11: /// </summary>
12: public class GreedyRoute : Route
13: {
14: #region Properties
15:
16: /// <summary>Gets the URL pattern for the route.</summary>
17: public new string Url { get; private set; }
18:
19: private LinkedList<GreedyRouteSegment> urlSegments = new LinkedList<GreedyRouteSegment>();
20:
21: private bool hasGreedySegment = false;
22:
23: /// <summary>Gets minimum number of segments that this route requires.</summary>
24: public int MinRequiredSegments { get; private set; }
25:
26: #endregion
27:
28: #region Constructors
29:
30: /// <summary>
31: /// Initializes a new instance of the <see cref="GreedyRoute"/> class, using the specified URL pattern and handler class.
32: /// </summary>
33: /// <param name="url">The URL pattern for the route.</param>
34: /// <param name="routeHandler">The object that processes requests for the route.</param>
35: public GreedyRoute(string url, IRouteHandler routeHandler)
36: : this(url, null, null, null, routeHandler)
37: {
38: }
39:
40: /// <summary>
41: /// Initializes a new instance of the <see cref="GreedyRoute"/> class, using the specified URL pattern, handler class, and default parameter values.
42: /// </summary>
43: /// <param name="url">The URL pattern for the route.</param>
44: /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
45: /// <param name="routeHandler">The object that processes requests for the route.</param>
46: public GreedyRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
47: : this(url, defaults, null, null, routeHandler)
48: {
49: }
50:
51: /// <summary>
52: /// Initializes a new instance of the <see cref="GreedyRoute"/> class, using the specified URL pattern, handler class, default parameter values, and constraints.
53: /// </summary>
54: /// <param name="url">The URL pattern for the route.</param>
55: /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
56: /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
57: /// <param name="routeHandler">The object that processes requests for the route.</param>
58: public GreedyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
59: : this(url, defaults, constraints, null, routeHandler)
60: {
61: }
62:
63: /// <summary>
64: /// Initializes a new instance of the <see cref="GreedyRoute"/> class, using the specified URL pattern, handler class, default parameter values, constraints, and custom values.
65: /// </summary>
66: /// <param name="url">The URL pattern for the route.</param>
67: /// <param name="defaults">The values to use if the URL does not contain all the parameters.</param>
68: /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param>
69: /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used to determine whether the route matches a specific URL pattern. The route handler might need these values to process the request.</param>
70: /// <param name="routeHandler">The object that processes requests for the route.</param>
71: public GreedyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
72: : base(url.Replace("*", ""), defaults, constraints, dataTokens, routeHandler)
73: {
74: this.Defaults = defaults ?? new RouteValueDictionary();
75: this.Constraints = constraints;
76: this.DataTokens = dataTokens;
77: this.RouteHandler = routeHandler;
78: this.Url = url;
79: this.MinRequiredSegments = 0;
80:
81: // URL must be defined
82: if (string.IsNullOrEmpty(url))
83: {
84: throw new ArgumentException("Route URL must be defined.", "url");
85: }
86:
87: // correct URL definition can have AT MOST ONE greedy segment
88: if (url.Split('*').Length > 2)
89: {
90: throw new ArgumentException("Route URL can have at most one greedy segment, but not more.", "url");
91: }
92:
93: Regex rx = new Regex(@"^(?<isToken>{)?(?(isToken)(?<isGreedy>\*?))(?<name>[a-zA-Z0-9-_]+)(?(isToken)})$", RegexOptions.Compiled | RegexOptions.Singleline);
94: foreach (string segment in url.Split('/'))
95: {
96: // segment must not be empty
97: if (string.IsNullOrEmpty(segment))
98: {
99: throw new ArgumentException("Route URL is invalid. Sequence \"//\" is not allowed.", "url");
100: }
101:
102: if (rx.IsMatch(segment))
103: {
104: Match m = rx.Match(segment);
105: GreedyRouteSegment s = new GreedyRouteSegment {
106: IsToken = m.Groups["isToken"].Value.Length.Equals(1),
107: IsGreedy = m.Groups["isGreedy"].Value.Length.Equals(1),
108: Name = m.Groups["name"].Value
109: };
110: this.urlSegments.AddLast(s);
111: this.hasGreedySegment |= s.IsGreedy;
112:
113: continue;
114: }
115: throw new ArgumentException("Route URL is invalid.", "url");
116: }
117:
118: // get minimum required segments for this route
119: LinkedListNode<GreedyRouteSegment> seg = this.urlSegments.Last;
120: int sIndex = this.urlSegments.Count;
121: while (seg != null && this.MinRequiredSegments.Equals(0))
122: {
123: if (!seg.Value.IsToken || !this.Defaults.ContainsKey(seg.Value.Name))
124: {
125: this.MinRequiredSegments = Math.Max(this.MinRequiredSegments, sIndex);
126: }
127: sIndex--;
128: seg = seg.Previous;
129: }
130:
131: // check that segments after greedy segment don't define a default
132: if (this.hasGreedySegment)
133: {
134: LinkedListNode<GreedyRouteSegment> s = this.urlSegments.Last;
135: while (s != null && !s.Value.IsGreedy)
136: {
137: if (s.Value.IsToken && this.Defaults.ContainsKey(s.Value.Name))
138: {
139: throw new ArgumentException(string.Format("Defaults for route segment \"{0}\" is not allowed, because it's specified after greedy catch-all segment.", s.Value.Name), "defaults");
140: }
141: s = s.Previous;
142: }
143: }
144: }
145:
146: #endregion
147:
148: #region GetRouteData
149: /// <summary>
150: /// Returns information about the requested route.
151: /// </summary>
152: /// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
153: /// <returns>
154: /// An object that contains the values from the route definition.
155: /// </returns>
156: public override RouteData GetRouteData(HttpContextBase httpContext)
157: {
158: string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + (httpContext.Request.PathInfo ?? string.Empty);
159:
160: RouteValueDictionary values = this.ParseRoute(virtualPath);
161: if (values == null)
162: {
163: return null;
164: }
165:
166: RouteData result = new RouteData(this, this.RouteHandler);
167: if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
168: {
169: return null;
170: }
171:
172: // everything's fine, fill route data
173: foreach (KeyValuePair<string, object> value in values)
174: {
175: result.Values.Add(value.Key, value.Value);
176: }
177: if (this.DataTokens != null)
178: {
179: foreach (KeyValuePair<string, object> token in this.DataTokens)
180: {
181: result.DataTokens.Add(token.Key, token.Value);
182: }
183: }
184: return result;
185: }
186: #endregion
187:
188: #region GetVirtualPath
189: /// <summary>
190: /// Returns information about the URL that is associated with the route.
191: /// </summary>
192: /// <param name="requestContext">An object that encapsulates information about the requested route.</param>
193: /// <param name="values">An object that contains the parameters for a route.</param>
194: /// <returns>
195: /// An object that contains information about the URL that is associated with the route.
196: /// </returns>
197: public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
198: {
199: RouteUrl url = this.Bind(requestContext.RouteData.Values, values);
200: if (url == null)
201: {
202: return null;
203: }
204: if (!this.ProcessConstraints(requestContext.HttpContext, url.Values, RouteDirection.UrlGeneration))
205: {
206: return null;
207: }
208:
209: VirtualPathData data = new VirtualPathData(this, url.Url);
210: if (this.DataTokens != null)
211: {
212: foreach (KeyValuePair<string, object> pair in this.DataTokens)
213: {
214: data.DataTokens[pair.Key] = pair.Value;
215: }
216: }
217: return data;
218: }
219: #endregion
220:
221: #region Private methods
222:
223: #region ProcessConstraints
224: /// <summary>
225: /// Processes constraints.
226: /// </summary>
227: /// <param name="httpContext">The HTTP context.</param>
228: /// <param name="values">Route values.</param>
229: /// <param name="direction">Route direction.</param>
230: /// <returns><c>true</c> if constraints are satisfied; otherwise, <c>false</c>.</returns>
231: private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection direction)
232: {
233: if (this.Constraints != null)
234: {
235: foreach (KeyValuePair<string, object> constraint in this.Constraints)
236: {
237: if (!this.ProcessConstraint(httpContext, constraint.Value, constraint.Key, values, direction))
238: {
239: return false;
240: }
241: }
242: }
243: return true;
244: }
245: #endregion
246:
247: #region ParseRoute
248: /// <summary>
249: /// Parses the route into segment data as defined by this route.
250: /// </summary>
251: /// <param name="virtualPath">Virtual path.</param>
252: /// <returns>Returns <see cref="System.Web.Routing.RouteValueDictionary"/> dictionary of route values.</returns>
253: private RouteValueDictionary ParseRoute(string virtualPath)
254: {
255: Stack<string> parts = new Stack<string>(
256: virtualPath
257: .Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
258: .Reverse() // we have to reverse it because parsing starts at the beginning not the end.
259: );
260:
261: // number of request route parts must match route URL definition
262: if (parts.Count < this.MinRequiredSegments)
263: {
264: return null;
265: }
266:
267: RouteValueDictionary result = new RouteValueDictionary();
268:
269: // start parsing from the beginning
270: bool finished = false;
271: LinkedListNode<GreedyRouteSegment> currentSegment = this.urlSegments.First;
272: while (!finished && !currentSegment.Value.IsGreedy)
273: {
274: object p = parts.Count > 0 ? parts.Pop() : null;
275: if (currentSegment.Value.IsToken)
276: {
277: p = p ?? this.Defaults[currentSegment.Value.Name];
278: result.Add(currentSegment.Value.Name, p);
279: }
280: else
281: {
282: if (!currentSegment.Value.Name.Equals(p))
283: {
284: return null;
285: }
286: }
287: currentSegment = currentSegment.Next;
288: finished = currentSegment == null;
289: }
290:
291: // continue from the end if needed
292: parts = new Stack<string>(parts); // this will reverse stack elements
293: currentSegment = this.urlSegments.Last;
294: while (!finished && !currentSegment.Value.IsGreedy)
295: {
296: object p = parts.Count > 0 ? parts.Pop() : null;
297: if (currentSegment.Value.IsToken)
298: {
299: p = p ?? this.Defaults[currentSegment.Value.Name];
300: result.Add(currentSegment.Value.Name, p);
301: }
302: else
303: {
304: if (!currentSegment.Value.Name.Equals(p))
305: {
306: return null;
307: }
308: }
309: currentSegment = currentSegment.Previous;
310: finished = currentSegment == null;
311: }
312:
313: // fill in the greedy catch-all segment
314: if (!finished)
315: {
316: object remaining = string.Join("/", parts.Reverse().ToArray()) ?? this.Defaults[currentSegment.Value.Name];
317: result.Add(currentSegment.Value.Name, remaining);
318: }
319:
320: // add remaining default values
321: foreach (KeyValuePair<string, object> def in this.Defaults)
322: {
323: if (!result.ContainsKey(def.Key))
324: {
325: result.Add(def.Key, def.Value);
326: }
327: }
328:
329: return result;
330: }
331: #endregion
332:
333: #region Bind
334: /// <summary>
335: /// Binds the specified current values and values into a URL.
336: /// </summary>
337: /// <param name="currentValues">Current route data values.</param>
338: /// <param name="values">Additional route values that can be used to generate the URL.</param>
339: /// <returns>Returns a URL route string.</returns>
340: private RouteUrl Bind(RouteValueDictionary currentValues, RouteValueDictionary values)
341: {
342: currentValues = currentValues ?? new RouteValueDictionary();
343: values = values ?? new RouteValueDictionary();
344:
345: HashSet<string> required = new HashSet<string>(this.urlSegments.Where(seg => seg.IsToken).ToList().ConvertAll(seg => seg.Name), StringComparer.OrdinalIgnoreCase);
346: RouteValueDictionary routeValues = new RouteValueDictionary();
347:
348: object dataValue = null;
349: foreach (string token in new List<string>(required))
350: {
351: dataValue = values[token] ?? currentValues[token] ?? this.Defaults[token];
352: if (this.IsUsable(dataValue))
353: {
354: string val = dataValue as string;
355: if (val != null)
356: {
357: val = val.StartsWith("/") ? val.Substring(1) : val;
358: val = val.EndsWith("/") ? val.Substring(0, val.Length - 1) : val;
359: }
360: routeValues.Add(token, val ?? dataValue);
361: required.Remove(token);
362: }
363: }
364:
365: // this route data is not related to this route
366: if (required.Count > 0)
367: {
368: return null;
369: }
370:
371: // add all remaining values
372: foreach (KeyValuePair<string, object> pair1 in values)
373: {
374: if (this.IsUsable(pair1.Value) && !routeValues.ContainsKey(pair1.Key))
375: {
376: routeValues.Add(pair1.Key, pair1.Value);
377: }
378: }
379:
380: // add remaining defaults
381: foreach (KeyValuePair<string, object> pair2 in this.Defaults)
382: {
383: if (this.IsUsable(pair2.Value) && !routeValues.ContainsKey(pair2.Key))
384: {
385: routeValues.Add(pair2.Key, pair2.Value);
386: }
387: }
388:
389: // check that non-segment defaults are the same as those provided
390: RouteValueDictionary nonRouteDefaults = new RouteValueDictionary(this.Defaults);
391: foreach (GreedyRouteSegment seg in this.urlSegments.Where(ss => ss.IsToken))
392: {
393: nonRouteDefaults.Remove(seg.Name);
394: }
395: foreach (KeyValuePair<string, object> pair3 in nonRouteDefaults)
396: {
397: if (!routeValues.ContainsKey(pair3.Key) || !this.RoutePartsEqual(pair3.Value, routeValues[pair3.Key]))
398: {
399: // route data is not related to this route
400: return null;
401: }
402: }
403:
404: StringBuilder sb = new StringBuilder();
405: RouteValueDictionary valuesToUse = new RouteValueDictionary(routeValues);
406: bool mustAdd = this.hasGreedySegment;
407:
408: // build URL string
409: LinkedListNode<GreedyRouteSegment> s = this.urlSegments.Last;
410: object segmentValue = null;
411: while (s != null)
412: {
413: if (s.Value.IsToken)
414: {
415: segmentValue = valuesToUse[s.Value.Name];
416: mustAdd = mustAdd || !this.RoutePartsEqual(segmentValue, this.Defaults[s.Value.Name]);
417: valuesToUse.Remove(s.Value.Name);
418: }
419: else
420: {
421: segmentValue = s.Value.Name;
422: mustAdd = true;
423: }
424:
425: if (mustAdd)
426: {
427: sb.Insert(0, sb.Length > 0 ? "/" : string.Empty);
428: sb.Insert(0, Uri.EscapeUriString(Convert.ToString(segmentValue, CultureInfo.InvariantCulture)));
429: }
430:
431: s = s.Previous;
432: }
433:
434: // add remaining values
435: if (valuesToUse.Count > 0)
436: {
437: bool first = true;
438: foreach (KeyValuePair<string, object> pair3 in valuesToUse)
439: {
440: // only add when different from defaults
441: if (!this.RoutePartsEqual(pair3.Value, this.Defaults[pair3.Key]))
442: {
443: sb.Append(first ? "?" : "&");
444: sb.Append(Uri.EscapeDataString(pair3.Key));
445: sb.Append("=");
446: sb.Append(Uri.EscapeDataString(Convert.ToString(pair3.Value, CultureInfo.InvariantCulture)));
447: first = false;
448: }
449: }
450: }
451:
452: return new RouteUrl {
453: Url = sb.ToString(),
454: Values = routeValues
455: };
456: }
457: #endregion
458:
459: #region IsUsable
460: /// <summary>
461: /// Determines whether an object actually is instantiated or has a value.
462: /// </summary>
463: /// <param name="value">Object value to check.</param>
464: /// <returns>
465: /// <c>true</c> if an object is instantiated or has a value; otherwise, <c>false</c>.
466: /// </returns>
467: private bool IsUsable(object value)
468: {
469: string val = value as string;
470: if (val != null)
471: {
472: return val.Length > 0;
473: }
474: return value != null;
475: }
476: #endregion
477:
478: #region RoutePartsEqual
479: /// <summary>
480: /// Checks if two route parts are equal
481: /// </summary>
482: /// <param name="firstValue">The first value.</param>
483: /// <param name="secondValue">The second value.</param>
484: /// <returns><c>true</c> if both values are equal; otherwise, <c>false</c>.</returns>
485: private bool RoutePartsEqual(object firstValue, object secondValue)
486: {
487: string sFirst = firstValue as string;
488: string sSecond = secondValue as string;
489: if ((sFirst != null) && (sSecond != null))
490: {
491: return string.Equals(sFirst, sSecond, StringComparison.OrdinalIgnoreCase);
492: }
493: if ((firstValue != null) && (secondValue != null))
494: {
495: return firstValue.Equals(secondValue);
496: }
497: return (firstValue == secondValue);
498: }
499: #endregion
500:
501: #endregion
502: }
503: }