예제 #1
0
        /// <summary>
        /// Builds a URL based on the Expression passed in
        /// </summary>
        /// <typeparam name="TController">Controller Type Only</typeparam>
        /// <param name="context">The current ViewContext</param>
        /// <param name="routeCollection">The <see cref="RouteCollection"/> to use for building the URL.</param>
        /// <param name="action">The action to invoke</param>
        /// <returns></returns>
        public static string BuildUrlFromExpression <TController>(RequestContext context, RouteCollection routeCollection, Expression <Action <TController> > action) where TController : Controller
        {
            // 4/4/2016 RL: We could not simply just use RouteValueDictionary routeValues = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action) because it is not Area aware;
            // so we had to get the guts of that method call and do it ourself to make it respect areas

            var body = action.Body as MethodCallExpression;

            Check.RequireNotNull(body, "MvcResources.ExpressionHelper_MustBeMethodCall");
            // ReSharper disable PossibleNullReferenceException
            var actionName = GetTargetActionName(body.Method);
            // ReSharper restore PossibleNullReferenceException

            var controllerType = typeof(TController);
            var controllerName = SitkaController.ControllerTypeToControllerName(controllerType);

            var routeValues = GetRouteValuesFromExpression(body, controllerType.Namespace, controllerName, actionName);

            var vpd = routeCollection.GetVirtualPath(context, routeValues);

            if (vpd == null)
            {
                return(null);
            }

            var routeUrl = vpd.VirtualPath;// Check if we are using DomainRoutes; really, Armstrong is the only one that does not use this.

            // Corral/LTInfo should be going this route
            if (routeCollection.Any(x => x is DomainRoute))
            {
                var route =
                    routeCollection.Where(x => x is DomainRoute).Cast <DomainRoute>()
                    .FirstOrDefault(
                        entry =>
                        entry.SitkaRouteTableEntry.Namespace == controllerType.Namespace && entry.SitkaRouteTableEntry.Controller == controllerName &&
                        entry.SitkaRouteTableEntry.Action == actionName);

                Check.Require(route != null,
                              $"Could not build a Url for Namespace \"{controllerType.Namespace}\", Controller \"{controllerName}\", Action \"{actionName}\" because no matching route was found");

                if (!route.SitkaRouteTableEntry.IsCrossAreaRoute && !string.IsNullOrWhiteSpace(route.Domain))
                {
                    return($"https://{route.Domain}{routeUrl}");
                }
            }
            return(routeUrl);
        }
예제 #2
0
        private static IEnumerable <SitkaRouteTableEntry> MakeRoutesForSingleMethod(Dictionary <string, string> areasDictionary, MethodInfo controllerActionMethod, Dictionary <string, List <string> > getMethodsDict)
        {
            var sitkaRouteEntries    = new List <SitkaRouteTableEntry>();
            var controller           = SitkaController.ControllerTypeToControllerName(controllerActionMethod.ReflectedType);
            var controllerPartForUrl = SitkaController.ControllerTypeToControllerNameForUrl(controllerActionMethod.ReflectedType);

            var isRestrictedToPost = IsRestrictedToPost(controllerActionMethod);

            var httpPostRouteSuffix = (isRestrictedToPost ? "__HttpPost" : "");
            var action = controllerActionMethod.Name;

            var routeName = string.Format("{0}__{1}{2}", controller, action, httpPostRouteSuffix);

            var allParameters = controllerActionMethod.GetParameters();

            var controllerAndAction = string.Format("{0}.{1}", controller, action);
            var parameterString     = String.Join(",", allParameters.Select(x => string.Format("{0} {1}", x.ParameterType.FullName, x.Name)));
            var actionControllerNameWithParameters = string.Format("{0}\t{1}\t{2}", controller, action, parameterString);

            if (!isRestrictedToPost)
            {
                AssertOptionalParametersAreOnlyAtEndOfRoute(routeName, allParameters);

                if (!getMethodsDict.ContainsKey(controllerAndAction))
                {
                    getMethodsDict.Add(controllerAndAction, new List <string>());
                }
                getMethodsDict[controllerAndAction].Add(actionControllerNameWithParameters);
            }
            else
            {
                if (getMethodsDict.ContainsKey(controllerAndAction))
                {
                    if (!getMethodsDict[controllerAndAction].Any(actionControllerNameWithParameters.StartsWith))
                    {
                        var allGetRoutes = String.Join("\r\n", getMethodsDict[controllerAndAction]);
                        throw new ApplicationException(string.Format("The POST route {0} must have a corresponding GET route with the same parameters\r\nPOST route: {1}\r\nGET routes: {2}",
                                                                     controllerAndAction,
                                                                     actionControllerNameWithParameters,
                                                                     allGetRoutes));
                    }
                }
            }

            // see if it has a route attribute defined; if so, use that as the route url
            var routeAttribute = controllerActionMethod.GetCustomAttributes <RouteAttribute>(false).SingleOrDefault();

            // Now add in all the variants of the route based on parameter overloading
            // /Foo.mvc/Action/1 => FooController.Action(1)
            var prependParameters = allParameters.Where(p => p.CustomAttributes.Any(ca => ca.AttributeType.IsAssignableFrom(typeof(PlaceUrlParameterBeforeControllerAndActionName)))).OrderBy(p => p.Position).ToList();
            var appendParameters  = allParameters.Except(prependParameters).OrderBy(p => p.Position).ToList();

            if (!prependParameters.Any())
            {
                var crossAreaRouteAttribute = controllerActionMethod.GetCustomAttributes <CrossAreaRouteAttribute>(false).SingleOrDefault();
                var isCrossAreaRoute        = crossAreaRouteAttribute != null;
                if (areasDictionary == null || !areasDictionary.Any())
                {
                    CreateSitkaTableRouteEntry(controllerActionMethod, appendParameters, routeAttribute, action, controller, routeName, sitkaRouteEntries, controllerPartForUrl, null, null, false);
                }
                else
                {
                    if (isCrossAreaRoute)
                    {
                        foreach (var areaKey in areasDictionary.Keys)
                        {
                            CreateSitkaTableRouteEntry(controllerActionMethod, appendParameters, routeAttribute, action, controller, routeName, sitkaRouteEntries, controllerPartForUrl, areaKey, areasDictionary[areaKey], true);
                        }
                    }
                    else
                    {
                        var area = SitkaController.ControllerTypeToAreaName(controllerActionMethod.ReflectedType);
                        Check.Require(areasDictionary.ContainsKey(area), $"Area \"{area}\" not found in Areas Dictionary!");
                        var areaAsSubdomainName = areasDictionary[area];
                        CreateSitkaTableRouteEntry(controllerActionMethod, appendParameters, routeAttribute, action, controller, routeName, sitkaRouteEntries, controllerPartForUrl, area, areaAsSubdomainName, false);
                    }
                }
            }
            else
            {
                // this is the Armstrong prepend the program identifier in the route path; as for now, we do not support Areas for it.
                var routeAppendVariants  = CalculateRouteParameterPermutationsAppend(appendParameters);
                var routePrependVariants = CalculateRouteParameterPermutationsPrepend(prependParameters);
                for (var appendIndex = 0; appendIndex < routeAppendVariants.Count; appendIndex++)
                {
                    sitkaRouteEntries.AddRange(
                        routePrependVariants.Select(
                            (t, prependIndex) =>
                    {
                        string routeUrl;
                        int?routeOrder;
                        var calculatedRouteName = $"{routeName}__{prependIndex:0000}__{appendIndex:0000}";
                        if (routeAttribute != null)
                        {
                            routeUrl   = routeAttribute.Template;
                            routeOrder = routeAttribute.Order;
                        }
                        else
                        {
                            routeUrl   = $"{t}{controllerPartForUrl}/{action}{routeAppendVariants[appendIndex]}";
                            routeOrder = null;
                        }

                        return(CreateSitkaTableRouteEntry(controllerActionMethod, action, controller, null, null, routeUrl, calculatedRouteName, routeOrder, false));
                    }));
                }
            }
            return(sitkaRouteEntries);
        }