示例#1
0
        public ReflectedApplicationModel BuildModel()
        {
            var applicationModel = new ReflectedApplicationModel();

            applicationModel.Filters.AddRange(_globalFilters);

            var assemblies      = _controllerAssemblyProvider.CandidateAssemblies;
            var types           = assemblies.SelectMany(a => a.DefinedTypes);
            var controllerTypes = types.Where(_conventions.IsController);

            foreach (var controllerType in controllerTypes)
            {
                var controllerModel = new ReflectedControllerModel(controllerType);
                applicationModel.Controllers.Add(controllerModel);

                foreach (var methodInfo in controllerType.AsType().GetMethods())
                {
                    var actionInfos = _conventions.GetActions(methodInfo, controllerType);
                    if (actionInfos == null)
                    {
                        continue;
                    }

                    foreach (var actionInfo in actionInfos)
                    {
                        var actionModel = new ReflectedActionModel(methodInfo);

                        actionModel.ActionName = actionInfo.ActionName;
                        actionModel.IsActionNameMatchRequired = actionInfo.RequireActionNameMatch;
                        actionModel.HttpMethods.AddRange(actionInfo.HttpMethods ?? Enumerable.Empty <string>());

                        if (actionInfo.AttributeRoute != null)
                        {
                            actionModel.AttributeRouteModel = new ReflectedAttributeRouteModel(
                                actionInfo.AttributeRoute);
                        }

                        foreach (var parameter in methodInfo.GetParameters())
                        {
                            actionModel.Parameters.Add(new ReflectedParameterModel(parameter));
                        }

                        controllerModel.Actions.Add(actionModel);
                    }
                }
            }

            return(applicationModel);
        }
        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);
        }
示例#3
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);
        }