Esempio n. 1
0
        private static ReflectedActionDescriptor CreateActionDescriptor(
            ReflectedActionModel action,
            ReflectedAttributeRouteModel controllerAttributeRoute,
            ControllerDescriptor controllerDescriptor)
        {
            var parameterDescriptors = new List <ParameterDescriptor>();

            foreach (var parameter in action.Parameters)
            {
                var isFromBody      = parameter.Attributes.OfType <FromBodyAttribute>().Any();
                var paramDescriptor = new ParameterDescriptor()
                {
                    Name       = parameter.ParameterName,
                    IsOptional = parameter.IsOptional
                };

                if (isFromBody)
                {
                    paramDescriptor.BodyParameterInfo = new BodyParameterInfo(
                        parameter.ParameterInfo.ParameterType);
                }
                else
                {
                    paramDescriptor.ParameterBindingInfo = new ParameterBindingInfo(
                        parameter.ParameterName,
                        parameter.ParameterInfo.ParameterType);
                }

                parameterDescriptors.Add(paramDescriptor);
            }

            var attributeRouteInfo = CreateAttributeRouteInfo(
                action.AttributeRouteModel,
                controllerAttributeRoute);

            var actionDescriptor = new ReflectedActionDescriptor()
            {
                Name = action.ActionName,
                ControllerDescriptor = controllerDescriptor,
                MethodInfo           = action.ActionMethod,
                Parameters           = parameterDescriptors,
                RouteConstraints     = new List <RouteDataActionConstraint>(),
                AttributeRouteInfo   = attributeRouteInfo
            };

            actionDescriptor.DisplayName = string.Format(
                "{0}.{1}",
                action.ActionMethod.DeclaringType.FullName,
                action.ActionMethod.Name);

            return(actionDescriptor);
        }
Esempio n. 2
0
        private static IList <ReflectedActionDescriptor> CreateActionDescriptors(
            ReflectedActionModel action,
            ReflectedControllerModel controller,
            ControllerDescriptor controllerDescriptor)
        {
            var actionDescriptors = new List <ReflectedActionDescriptor>();

            // We check the action to see if the template allows combination behavior
            // (It doesn't start with / or ~/) so that in the case where we have multiple
            // [Route] attributes on the controller we don't end up creating multiple
            // attribute identical attribute routes.
            if (controller.AttributeRoutes != null &&
                controller.AttributeRoutes.Count > 0 &&
                (action.AttributeRouteModel == null ||
                 !action.AttributeRouteModel.IsAbsoluteTemplate))
            {
                foreach (var controllerAttributeRoute in controller.AttributeRoutes)
                {
                    var actionDescriptor = CreateActionDescriptor(
                        action,
                        controllerAttributeRoute,
                        controllerDescriptor);

                    actionDescriptors.Add(actionDescriptor);
                }
            }
            else
            {
                actionDescriptors.Add(CreateActionDescriptor(
                                          action,
                                          controllerAttributeRoute: null,
                                          controllerDescriptor: controllerDescriptor));
            }

            return(actionDescriptors);
        }
        public List<ReflectedActionDescriptor> Build(ReflectedApplicationModel model)
        {
            var actions = new List<ReflectedActionDescriptor>();

            var routeGroupsByTemplate = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            var removalConstraints = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

            var routeTemplateErrors = new List<string>();

            foreach (var controller in model.Controllers)
            {
                var controllerDescriptor = new ControllerDescriptor(controller.ControllerType);
                foreach (var action in controller.Actions)
                {
                    var parameterDescriptors = new List<ParameterDescriptor>();
                    foreach (var parameter in action.Parameters)
                    {
                        var isFromBody = parameter.Attributes.OfType<FromBodyAttribute>().Any();

                        parameterDescriptors.Add(new ParameterDescriptor()
                        {
                            Name = parameter.ParameterName,
                            IsOptional = parameter.IsOptional,

                            ParameterBindingInfo = isFromBody
                                ? null
                                : new ParameterBindingInfo(
                                    parameter.ParameterName,
                                    parameter.ParameterInfo.ParameterType),

                            BodyParameterInfo = isFromBody
                                ? new BodyParameterInfo(parameter.ParameterInfo.ParameterType)
                                : null
                        });
                    }

                    var actionDescriptor = new ReflectedActionDescriptor()
                    {
                        Name = action.ActionName,
                        ControllerDescriptor = controllerDescriptor,
                        MethodInfo = action.ActionMethod,
                        Parameters = parameterDescriptors,
                        RouteConstraints = new List<RouteDataActionConstraint>(),
                    };

                    actionDescriptor.DisplayName = string.Format(
                        "{0}.{1}",
                        action.ActionMethod.DeclaringType.FullName,
                        action.ActionMethod.Name);

                    var httpMethods = action.HttpMethods;
                    if (httpMethods != null && httpMethods.Count > 0)
                    {
                        actionDescriptor.MethodConstraints = new List<HttpMethodConstraint>()
                        {
                            new HttpMethodConstraint(httpMethods)
                        };
                    }

                    actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                        "controller",
                        controller.ControllerName));

                    if (action.IsActionNameMatchRequired)
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                            "action",
                            action.ActionName));
                    }
                    else
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                            "action",
                            RouteKeyHandling.DenyKey));
                    }

                    foreach (var constraintAttribute in controller.RouteConstraints)
                    {
                        if (constraintAttribute.BlockNonAttributedActions)
                        {
                            removalConstraints.Add(constraintAttribute.RouteKey);
                        }

                        // Skip duplicates
                        if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                constraintAttribute.RouteKey,
                                constraintAttribute.RouteValue));
                        }
                    }

                    var templateText = AttributeRouteTemplate.Combine(
                            controller.RouteTemplate,
                            action.RouteTemplate);

                    if (templateText != null)
                    {
                        // An attribute routed action will ignore conventional routed constraints. We still
                        // want to provide these values as ambient values.
                        foreach (var constraint in actionDescriptor.RouteConstraints)
                        {
                            actionDescriptor.RouteValueDefaults.Add(constraint.RouteKey, constraint.RouteValue);
                        }

                        // Replaces tokens like [controller]/[action] in the route template with the actual values
                        // for this action.
                        try
                        {
                            templateText = AttributeRouteTemplate.ReplaceTokens(
                                templateText,
                                actionDescriptor.RouteValueDefaults);
                        }
                        catch (InvalidOperationException ex)
                        {
                            var message = Resources.FormatAttributeRoute_IndividualErrorMessage(
                                actionDescriptor.DisplayName,
                                Environment.NewLine,
                                ex.Message);

                            routeTemplateErrors.Add(message);
                        }

                        actionDescriptor.AttributeRouteTemplate = templateText;

                        // An attribute routed action is matched by its 'route group' which identifies all equivalent
                        // actions.
                        string routeGroup;
                        if (!routeGroupsByTemplate.TryGetValue(templateText, out routeGroup))
                        {
                            routeGroup = GetRouteGroup(templateText);
                            routeGroupsByTemplate.Add(templateText, routeGroup);
                        }

                        var routeConstraints = new List<RouteDataActionConstraint>();
                        routeConstraints.Add(new RouteDataActionConstraint(
                            AttributeRouting.RouteGroupKey,
                            routeGroup));

                        actionDescriptor.RouteConstraints = routeConstraints;
                    }

                    actionDescriptor.FilterDescriptors =
                        action.Filters.Select(f => new FilterDescriptor(f, FilterScope.Action))
                        .Concat(controller.Filters.Select(f => new FilterDescriptor(f, FilterScope.Controller)))
                        .Concat(model.Filters.Select(f => new FilterDescriptor(f, FilterScope.Global)))
                        .OrderBy(d => d, FilterDescriptorOrderComparer.Comparer)
                        .ToList();

                    actions.Add(actionDescriptor);
                }
            }

            foreach (var actionDescriptor in actions)
            {
                foreach (var key in removalConstraints)
                {
                    if (actionDescriptor.AttributeRouteTemplate == null)
                    {
                        // Any any attribute routes are in use, then non-attribute-routed ADs can't be selected
                        // when a route group returned by the route.
                        if (routeGroupsByTemplate.Any())
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                AttributeRouting.RouteGroupKey,
                                RouteKeyHandling.DenyKey));
                        }

                        if (!HasConstraint(actionDescriptor.RouteConstraints, key))
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                key,
                                RouteKeyHandling.DenyKey));
                        }
                    }
                    else
                    {
                        // We still want to add a 'null' for any constraint with DenyKey so that link generation
                        // works properly.
                        //
                        // Consider an action like { area = "", controller = "Home", action = "Index" }. Even if
                        // it's attribute routed, it needs to know that area must be null to generate a link.
                        if (!actionDescriptor.RouteValueDefaults.ContainsKey(key))
                        {
                            actionDescriptor.RouteValueDefaults.Add(key, null);
                        }
                    }
                }
            }

            if (routeTemplateErrors.Any())
            {
                var message = Resources.FormatAttributeRoute_AggregateErrorMessage(
                    Environment.NewLine,
                    string.Join(Environment.NewLine + Environment.NewLine, routeTemplateErrors));
                throw new InvalidOperationException(message);
            }

            return actions;
        }
        public List <ReflectedActionDescriptor> Build(ReflectedApplicationModel model)
        {
            var actions = new List <ReflectedActionDescriptor>();

            var routeGroupsByTemplate = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
            var removalConstraints    = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            var routeTemplateErrors = new List <string>();

            foreach (var controller in model.Controllers)
            {
                var controllerDescriptor = new ControllerDescriptor(controller.ControllerType);
                foreach (var action in controller.Actions)
                {
                    var parameterDescriptors = new List <ParameterDescriptor>();
                    foreach (var parameter in action.Parameters)
                    {
                        var isFromBody = parameter.Attributes.OfType <FromBodyAttribute>().Any();

                        parameterDescriptors.Add(new ParameterDescriptor()
                        {
                            Name       = parameter.ParameterName,
                            IsOptional = parameter.IsOptional,

                            ParameterBindingInfo = isFromBody
                                ? null
                                : new ParameterBindingInfo(
                                parameter.ParameterName,
                                parameter.ParameterInfo.ParameterType),

                            BodyParameterInfo = isFromBody
                                ? new BodyParameterInfo(parameter.ParameterInfo.ParameterType)
                                : null
                        });
                    }

                    var actionDescriptor = new ReflectedActionDescriptor()
                    {
                        Name = action.ActionName,
                        ControllerDescriptor = controllerDescriptor,
                        MethodInfo           = action.ActionMethod,
                        Parameters           = parameterDescriptors,
                        RouteConstraints     = new List <RouteDataActionConstraint>(),
                    };

                    actionDescriptor.DisplayName = string.Format(
                        "{0}.{1}",
                        action.ActionMethod.DeclaringType.FullName,
                        action.ActionMethod.Name);

                    var httpMethods = action.HttpMethods;
                    if (httpMethods != null && httpMethods.Count > 0)
                    {
                        actionDescriptor.MethodConstraints = new List <HttpMethodConstraint>()
                        {
                            new HttpMethodConstraint(httpMethods)
                        };
                    }

                    actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                              "controller",
                                                              controller.ControllerName));

                    if (action.IsActionNameMatchRequired)
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                  "action",
                                                                  action.ActionName));
                    }
                    else
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                  "action",
                                                                  RouteKeyHandling.DenyKey));
                    }

                    foreach (var constraintAttribute in controller.RouteConstraints)
                    {
                        if (constraintAttribute.BlockNonAttributedActions)
                        {
                            removalConstraints.Add(constraintAttribute.RouteKey);
                        }

                        // Skip duplicates
                        if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                      constraintAttribute.RouteKey,
                                                                      constraintAttribute.RouteValue));
                        }
                    }

                    var templateText = AttributeRouteTemplate.Combine(
                        controller.RouteTemplate,
                        action.RouteTemplate);

                    if (templateText != null)
                    {
                        // An attribute routed action will ignore conventional routed constraints. We still
                        // want to provide these values as ambient values.
                        foreach (var constraint in actionDescriptor.RouteConstraints)
                        {
                            actionDescriptor.RouteValueDefaults.Add(constraint.RouteKey, constraint.RouteValue);
                        }

                        // Replaces tokens like [controller]/[action] in the route template with the actual values
                        // for this action.
                        try
                        {
                            templateText = AttributeRouteTemplate.ReplaceTokens(
                                templateText,
                                actionDescriptor.RouteValueDefaults);
                        }
                        catch (InvalidOperationException ex)
                        {
                            var message = Resources.FormatAttributeRoute_IndividualErrorMessage(
                                actionDescriptor.DisplayName,
                                Environment.NewLine,
                                ex.Message);

                            routeTemplateErrors.Add(message);
                        }

                        actionDescriptor.AttributeRouteTemplate = templateText;

                        // An attribute routed action is matched by its 'route group' which identifies all equivalent
                        // actions.
                        string routeGroup;
                        if (!routeGroupsByTemplate.TryGetValue(templateText, out routeGroup))
                        {
                            routeGroup = GetRouteGroup(templateText);
                            routeGroupsByTemplate.Add(templateText, routeGroup);
                        }

                        var routeConstraints = new List <RouteDataActionConstraint>();
                        routeConstraints.Add(new RouteDataActionConstraint(
                                                 AttributeRouting.RouteGroupKey,
                                                 routeGroup));

                        actionDescriptor.RouteConstraints = routeConstraints;
                    }

                    actionDescriptor.FilterDescriptors =
                        action.Filters.Select(f => new FilterDescriptor(f, FilterScope.Action))
                        .Concat(controller.Filters.Select(f => new FilterDescriptor(f, FilterScope.Controller)))
                        .Concat(model.Filters.Select(f => new FilterDescriptor(f, FilterScope.Global)))
                        .OrderBy(d => d, FilterDescriptorOrderComparer.Comparer)
                        .ToList();

                    actions.Add(actionDescriptor);
                }
            }

            foreach (var actionDescriptor in actions)
            {
                foreach (var key in removalConstraints)
                {
                    if (actionDescriptor.AttributeRouteTemplate == null)
                    {
                        // Any any attribute routes are in use, then non-attribute-routed ADs can't be selected
                        // when a route group returned by the route.
                        if (routeGroupsByTemplate.Any())
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                      AttributeRouting.RouteGroupKey,
                                                                      RouteKeyHandling.DenyKey));
                        }

                        if (!HasConstraint(actionDescriptor.RouteConstraints, key))
                        {
                            actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                      key,
                                                                      RouteKeyHandling.DenyKey));
                        }
                    }
                    else
                    {
                        // We still want to add a 'null' for any constraint with DenyKey so that link generation
                        // works properly.
                        //
                        // Consider an action like { area = "", controller = "Home", action = "Index" }. Even if
                        // it's attribute routed, it needs to know that area must be null to generate a link.
                        if (!actionDescriptor.RouteValueDefaults.ContainsKey(key))
                        {
                            actionDescriptor.RouteValueDefaults.Add(key, null);
                        }
                    }
                }
            }

            if (routeTemplateErrors.Any())
            {
                var message = Resources.FormatAttributeRoute_AggregateErrorMessage(
                    Environment.NewLine,
                    string.Join(Environment.NewLine + Environment.NewLine, routeTemplateErrors));
                throw new InvalidOperationException(message);
            }

            return(actions);
        }
Esempio n. 5
0
        public List <ReflectedActionDescriptor> Build(ReflectedApplicationModel model)
        {
            var actions = new List <ReflectedActionDescriptor>();

            var hasAttributeRoutes = false;
            var removalConstraints = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            var methodInfoMap = new MethodToActionMap();

            var routeTemplateErrors = new List <string>();
            var attributeRoutingConfigurationErrors = new Dictionary <MethodInfo, string>();

            foreach (var controller in model.Controllers)
            {
                var controllerDescriptor = new ControllerDescriptor(controller.ControllerType);
                foreach (var action in controller.Actions)
                {
                    // Controllers with multiple [Route] attributes (or user defined implementation of
                    // IRouteTemplateProvider) will generate one action descriptor per IRouteTemplateProvider
                    // instance.
                    // Actions with multiple [Http*] attributes or other (IRouteTemplateProvider implementations
                    // have already been identified as different actions during action discovery.
                    var actionDescriptors = CreateActionDescriptors(action, controller, controllerDescriptor);

                    foreach (var actionDescriptor in actionDescriptors)
                    {
                        AddActionFilters(actionDescriptor, action.Filters, controller.Filters, model.Filters);
                        AddActionConstraints(actionDescriptor, action, controller);
                        AddControllerRouteConstraints(
                            actionDescriptor,
                            controller.RouteConstraints,
                            removalConstraints);

                        if (IsAttributeRoutedAction(actionDescriptor))
                        {
                            hasAttributeRoutes = true;

                            // An attribute routed action will ignore conventional routed constraints. We still
                            // want to provide these values as ambient values for link generation.
                            AddConstraintsAsDefaultRouteValues(actionDescriptor);

                            // Replaces tokens like [controller]/[action] in the route template with the actual values
                            // for this action.
                            ReplaceAttributeRouteTokens(actionDescriptor, routeTemplateErrors);

                            // Attribute routed actions will ignore conventional routed constraints. Instead they have
                            // a single route constraint "RouteGroup" associated with it.
                            ReplaceRouteConstraints(actionDescriptor);
                        }
                    }

                    methodInfoMap.AddToMethodInfo(action, actionDescriptors);
                    actions.AddRange(actionDescriptors);
                }
            }

            var actionsByRouteName = new Dictionary <string, IList <ActionDescriptor> >(
                StringComparer.OrdinalIgnoreCase);

            // Keeps track of all the methods that we've validated to avoid visiting each action group
            // more than once.
            var validatedMethods = new HashSet <MethodInfo>();

            foreach (var actionDescriptor in actions)
            {
                if (!validatedMethods.Contains(actionDescriptor.MethodInfo))
                {
                    ValidateActionGroupConfiguration(
                        methodInfoMap,
                        actionDescriptor,
                        attributeRoutingConfigurationErrors);

                    validatedMethods.Add(actionDescriptor.MethodInfo);
                }

                if (!IsAttributeRoutedAction(actionDescriptor))
                {
                    // Any attribute routes are in use, then non-attribute-routed action descriptors can't be
                    // selected when a route group returned by the route.
                    if (hasAttributeRoutes)
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                  AttributeRouting.RouteGroupKey,
                                                                  RouteKeyHandling.DenyKey));
                    }

                    // Add a route constraint with DenyKey for each constraint in the set to all the
                    // actions that don't have that constraint. For example, if a controller defines
                    // an area constraint, all actions that don't belong to an area must have a route
                    // constraint that prevents them from matching an incomming request.
                    AddRemovalConstraints(actionDescriptor, removalConstraints);
                }
                else
                {
                    var attributeRouteInfo = actionDescriptor.AttributeRouteInfo;
                    if (attributeRouteInfo.Name != null)
                    {
                        // Build a map of attribute route name to action descriptors to ensure that all
                        // attribute routes with a given name have the same template.
                        AddActionToNamedGroup(actionsByRouteName, attributeRouteInfo.Name, actionDescriptor);
                    }

                    // We still want to add a 'null' for any constraint with DenyKey so that link generation
                    // works properly.
                    //
                    // Consider an action like { area = "", controller = "Home", action = "Index" }. Even if
                    // it's attribute routed, it needs to know that area must be null to generate a link.
                    foreach (var key in removalConstraints)
                    {
                        if (!actionDescriptor.RouteValueDefaults.ContainsKey(key))
                        {
                            actionDescriptor.RouteValueDefaults.Add(key, null);
                        }
                    }
                }
            }

            if (attributeRoutingConfigurationErrors.Any())
            {
                var message = CreateAttributeRoutingAggregateErrorMessage(
                    attributeRoutingConfigurationErrors.Values);

                throw new InvalidOperationException(message);
            }

            var namedRoutedErrors = ValidateNamedAttributeRoutedActions(actionsByRouteName);

            if (namedRoutedErrors.Any())
            {
                var message = CreateAttributeRoutingAggregateErrorMessage(namedRoutedErrors);
                throw new InvalidOperationException(message);
            }

            if (routeTemplateErrors.Any())
            {
                var message = CreateAttributeRoutingAggregateErrorMessage(routeTemplateErrors);
                throw new InvalidOperationException(message);
            }

            return(actions);
        }