2016-07-19 8 views
0

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: } 

Antwort

0

Wie Routing-Tabelle alle möglichen Kombinationen hat, trotzte da Route ist nicht statisch, dynamisch.

Routen werden in der statischen RouteTable.Routes Tabelle definiert. Daher werden sie nur bei der Anwendungsinitialisierung instanziiert. Das einzige, was Sie im Konstruktor tun können, ist Werte und Dienste zu speichern, die zur Laufzeit zur Auswertung verwendet werden können.

Zur Laufzeit ist der Konstruktor nie laufen. Routen werden in der Routentabelle von der ersten bis zur letzten Route ausgewertet. Wenn jedoch eine übereinstimmende Route gefunden wird, verwendet das Routing diese Route und fährt nicht weiter fort. Mit anderen Worten, das erste Spiel gewinnt.

dieses Beispiel vor:

routes.Add(new ABCRoute()); 
routes.Add(new XYZRoute()); 

MVC wird immer Versuch, die ABCRoute erste und anzupassen, wenn ABCRoute neben der XYZRoute übereinstimmen fehlgeschlagen.

Eine Route wird als Übereinstimmung betrachtet, wenn sie einen Wert nicht null zurückgibt. Also, die Logik in Ihrer Route muss immer zurückgeben null, wenn es bestimmt, dass es die Anfrage nicht behandeln sollte. Dadurch wird das Routing-Framework die nächste Route testen, die für eine Übereinstimmung registriert ist.

Wenn eine Route übereinstimmt, sollte sie die Routenwerte zurückgeben, die MVC mitteilen, welcher Controller und welche Aktion gesucht werden soll (zusammen mit allen Parametern).

HINWEIS: Sie nichts annehmen kann über das, was die ABCRoute auf der Tatsache nicht aus, dass es erbt GreedyRoute, da ABCRoute vollständig seine Super Verhalten neu definieren kann.