/* **************************************************************************** * * Copyright (c) Microsoft Corporation. All rights reserved. * * This software is subject to the Microsoft Public License (Ms-PL). * A copy of the license can be found in the license.htm file included * in this distribution. * * You must not remove this notice, or any other, from this software. * * ***************************************************************************/ namespace System.Web.Mvc { using System; using System.Diagnostics.CodeAnalysis; using System.Web.Routing; public static class RouteCollectionExtensions { // This method returns a new RouteCollection containing only routes that matched a particular area. // The Boolean out parameter is just a flag specifying whether any registered routes were area-aware. private static RouteCollection FilterRouteCollectionByArea(RouteCollection routes, string areaName, out bool usingAreas) { if (areaName == null) { areaName = String.Empty; } usingAreas = false; RouteCollection filteredRoutes = new RouteCollection(); using (routes.GetReadLock()) { foreach (RouteBase route in routes) { string thisAreaName = AreaHelpers.GetAreaName(route) ?? String.Empty; usingAreas |= (thisAreaName.Length > 0); if (String.Equals(thisAreaName, areaName, StringComparison.OrdinalIgnoreCase)) { filteredRoutes.Add(route); } } } // if areas are not in use, the filtered route collection might be incorrect return (usingAreas) ? filteredRoutes : routes; } public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, RouteValueDictionary values) { return GetVirtualPathForArea(routes, requestContext, null /* name */, values); } public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values) { bool usingAreas; // don't care about this value return GetVirtualPathForArea(routes, requestContext, name, values, out usingAreas); } internal static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values, out bool usingAreas) { if (routes == null) { throw new ArgumentNullException("routes"); } if (!String.IsNullOrEmpty(name)) { // the route name is a stronger qualifier than the area name, so just pipe it through usingAreas = false; return routes.GetVirtualPath(requestContext, name, values); } string targetArea = null; if (values != null) { object targetAreaRawValue; if (values.TryGetValue("area", out targetAreaRawValue)) { targetArea = targetAreaRawValue as string; } else { // set target area to current area if (requestContext != null) { targetArea = AreaHelpers.GetAreaName(requestContext.RouteData); } } } // need to apply a correction to the RVD if areas are in use RouteValueDictionary correctedValues = values; RouteCollection filteredRoutes = FilterRouteCollectionByArea(routes, targetArea, out usingAreas); if (usingAreas) { correctedValues = new RouteValueDictionary(values); correctedValues.Remove("area"); } VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues); return vpd; } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static void IgnoreRoute(this RouteCollection routes, string url) { IgnoreRoute(routes, url, null /* constraints */); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static void IgnoreRoute(this RouteCollection routes, string url, object constraints) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } IgnoreRouteInternal route = new IgnoreRouteInternal(url) { Constraints = new RouteValueDictionary(constraints) }; routes.Add(route); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url) { return MapRoute(routes, name, url, null /* defaults */, (object)null /* constraints */); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults) { return MapRoute(routes, name, url, defaults, (object)null /* constraints */); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) { return MapRoute(routes, name, url, defaults, constraints, null /* namespaces */); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces) { return MapRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) { return MapRoute(routes, name, url, defaults, null /* constraints */, namespaces); } [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } private sealed class IgnoreRouteInternal : Route { public IgnoreRouteInternal(string url) : base(url, new StopRoutingHandler()) { } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues) { // Never match during route generation. This avoids the scenario where an IgnoreRoute with // fairly relaxed constraints ends up eagerly matching all generated URLs. return null; } } } }