使用RouteConfig类的RegisterRoutes方法中的routes.add定义自定义路由。
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()));
ABCRoute是一个继承Route类的类。
ABCClass类似于以下链接中的GreedyClass,如下所示。 http://erraticdev.blogspot.com/2011/01/custom-aspnet-mvc-route-class-with.html
on对应用程序的第一个请求,调用带有参数的构造函数,它不返回任何内容。
当我们调用上面给出的Route.Add()方法时,如何定义路由并构建路由表。
路由表如何具有所有可能的组合,因为路由defied是动态的而不是静态的。
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: }
答案 0 :(得分:0)
路由表如何具有所有可能的组合,因为路由defied是动态的而不是静态的。
路由在静态 RouteTable.Routes
表中定义。因此,它们仅在应用程序初始化时实例化。您可以在构造函数中做的唯一事情是保存值和服务,可以在运行时用于评估。
在运行时,构造函数从不运行。路由表从第一条路径到最后一条路径进行评估。但是,当找到匹配的路由时,路由将使用该路由而不再继续。换句话说,第一场比赛获胜。
考虑这个例子:
routes.Add(new ABCRoute());
routes.Add(new XYZRoute());
MVC将总是首先尝试匹配ABCRoute
,如果ABCRoute
无法匹配,则XYZRoute
接下来匹配。
如果路线返回非空值,则该路线被视为匹配。因此,如果路由中的逻辑确定它不应该处理请求,那么它必须始终返回null
。这将使路由框架测试为匹配注册的下一个路由。
如果路由匹配,则应返回路由值,告诉MVC要查找哪个控制器和操作(以及任何参数)。
注意:基于
ABCRoute
继承GreedyRoute
的事实,您不能假设任何ABCRoute
,因为//method to generate signature //$this->method = "GET" //QBO_SANDBOX_URL = 'https://some_url.com/' //$this->_query = 'something=something' public function generate_signature() { $base = $this->_method.'&'.rawurlencode($this->_url.QBO_SANDBOX_URL.'v3/company/'.$this->_realm_id).'&' .rawurlencode("oauth_consumer_key=".rawurlencode($this->_consumer_key).'&' .'&oauth_nonce='.rawurlencode('34604g54654y456546') .'&oauth_signature_method='.rawurlencode('HMAC-SHA1') .'&oauth_timestamp='.rawurlencode(time()) .'&oauth_token='.rawurlencode($this->_auth_token) .'&oauth_version='.rawurlencode('1.0') .'&'.rawurlencode($this->_query)); $key = rawurlencode($this->_consumer_secret.'&'.$this->_token_secret); $this->_signature = base64_encode(hash_hmac("sha1", $base, $key, true)); }
}可以完全重新定义它的超类行为。