public void Build_WithPropertiesSet_ActionOverwritesApplicationAndControllerModel()
        {
            // Arrange
            var applicationModel = new ApplicationModel();
            applicationModel.Properties["test"] = "application";

            var controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
                                                 new List<object>() { });
            controller.Application = applicationModel;
            controller.Properties["test"] = "controller";
            applicationModel.Controllers.Add(controller);

            var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
            var actionModel = new ActionModel(methodInfo, new List<object>() { });
            actionModel.Selectors.Add(new SelectorModel());
            actionModel.Controller = controller;
            actionModel.Properties["test"] = "action";
            controller.Actions.Add(actionModel);

            // Act
            var descriptors = ControllerActionDescriptorBuilder.Build(applicationModel);

            // Assert
            Assert.Equal("action", descriptors.Single().Properties["test"]);
        }
        public void Apply(ControllerModel controller)
        {
            if (controller == null)
            {
                throw new ArgumentNullException(nameof(controller));
            }

            if (IsConventionApplicable(controller))
            {
                var newActions = new List<ActionModel>();

                foreach (var action in controller.Actions)
                {
                    SetHttpMethodFromConvention(action);

                    // Action Name doesn't really come into play with attribute routed actions. However for a
                    // non-attribute-routed action we need to create a 'named' version and an 'unnamed' version.
                    if (!IsActionAttributeRouted(action))
                    {
                        var namedAction = action;

                        var unnamedAction = new ActionModel(namedAction);
                        unnamedAction.RouteValues.Add("action", null);
                        newActions.Add(unnamedAction);
                    }
                }

                foreach (var action in newActions)
                {
                    controller.Actions.Add(action);
                }
            }
        }
Пример #3
0
        public void CopyConstructor_DoesDeepCopyOfOtherModels()
        {
            // Arrange
            var action = new ActionModel(typeof(TestController).GetMethod(nameof(TestController.Edit)),
                                         new List<object>());

            var parameter = new ParameterModel(action.ActionMethod.GetParameters()[0],
                                               new List<object>());
            parameter.Action = action;
            action.Parameters.Add(parameter);

            var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
            action.Selectors.Add(new SelectorModel()
            {
                AttributeRouteModel = route
            });

            var apiExplorer = action.ApiExplorer;
            apiExplorer.IsVisible = false;
            apiExplorer.GroupName = "group1";

            // Act
            var action2 = new ActionModel(action);

            // Assert
            Assert.NotSame(action, action2.Parameters[0]);
            Assert.NotSame(apiExplorer, action2.ApiExplorer);
            Assert.NotSame(action.Selectors, action2.Selectors);
            Assert.NotNull(action2.Selectors);
            Assert.Single(action2.Selectors);
            Assert.NotSame(route, action2.Selectors[0].AttributeRouteModel);
        }
Пример #4
0
 public void Apply(ActionModel action)
 {
     foreach (var param in action.Parameters)
     {
         if (param.Attributes.Any(p => p.GetType() == typeof(FromHeaderAttribute)))
         {
             param.Action.Properties["source"] = "From Header";
         }
     }
 }
        public void Apply(ActionModel action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (IsConventionApplicable(action.Controller))
            {
                var optionalParameters = new HashSet<string>();
                var uriBindingSource = (new FromUriAttribute()).BindingSource;
                foreach (var parameter in action.Parameters)
                {
                    // Some IBindingSourceMetadata attributes like ModelBinder attribute return null
                    // as their binding source. Special case to ensure we do not ignore them.
                    if (parameter.BindingInfo?.BindingSource != null ||
                        parameter.Attributes.OfType<IBindingSourceMetadata>().Any())
                    {
                        // This has a binding behavior configured, just leave it alone.
                    }
                    else if (CanConvertFromString(parameter.ParameterInfo.ParameterType))
                    {
                        // Simple types are by-default from the URI.
                        parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
                        parameter.BindingInfo.BindingSource = uriBindingSource;
                    }
                    else
                    {
                        // Complex types are by-default from the body.
                        parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
                        parameter.BindingInfo.BindingSource = BindingSource.Body;
                    }

                    // For all non IOptionalBinderMetadata, which are not URL source (like FromQuery etc.) do not
                    // participate in overload selection and hence are added to the hashset so that they can be
                    // ignored in OverloadActionConstraint.
                    var optionalMetadata = parameter.Attributes.OfType<IOptionalBinderMetadata>().SingleOrDefault();
                    if (parameter.ParameterInfo.HasDefaultValue && parameter.BindingInfo.BindingSource == uriBindingSource ||
                        optionalMetadata != null && optionalMetadata.IsOptional ||
                        optionalMetadata == null && parameter.BindingInfo.BindingSource != uriBindingSource)
                    {
                        optionalParameters.Add(parameter.ParameterName);
                    }
                }

                action.Properties.Add("OptionalParameters", optionalParameters);
            }
        }
        public void Apply(ActionModel action)
        {
            if (!action.Controller.ControllerType.IsSubclassOf(typeof(ServiceEndpoint)))
                return;

            foreach (var parameter in action.Parameters)
            {
                var paramType = parameter.ParameterInfo.ParameterType;
                if (typeof(ServiceRequest).IsAssignableFrom(typeof(ServiceRequest)))
                {
                    if (!action.Filters.Any(x => x is JsonFilter))
                        action.Filters.Add(new JsonFilter());

                    break;
                }
            }
        }
        public void Build_WithControllerPropertiesSet_AddsPropertiesWithBinderMetadataSet()
        {
            // Arrange
            var applicationModel = new ApplicationModel();
            var controller = new ControllerModel(
                typeof(TestController).GetTypeInfo(),
                new List<object>() { });

            var propertyInfo = controller.ControllerType.AsType().GetProperty("BoundProperty");
            controller.ControllerProperties.Add(
                new PropertyModel(
                    propertyInfo,
                    new List<object>() { })
                {
                    BindingInfo = BindingInfo.GetBindingInfo(new object[] { new FromQueryAttribute() }),
                    PropertyName = "BoundProperty"
                });

            controller.ControllerProperties.Add(
               new PropertyModel(
                   controller.ControllerType.AsType().GetProperty("UnboundProperty"),
                   new List<object>() { }));

            controller.Application = applicationModel;
            applicationModel.Controllers.Add(controller);

            var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
            var actionModel = new ActionModel(methodInfo, new List<object>() { });
            actionModel.Selectors.Add(new SelectorModel());
            actionModel.Controller = controller;
            controller.Actions.Add(actionModel);

            // Act
            var descriptors = ControllerActionDescriptorBuilder.Build(applicationModel);

            // Assert
            var controllerDescriptor = Assert.Single(descriptors);

            var parameter = Assert.Single(controllerDescriptor.BoundProperties);
            var property = Assert.IsType<ControllerBoundPropertyDescriptor>(parameter);
            Assert.Equal("BoundProperty", property.Name);
            Assert.Equal(propertyInfo, property.PropertyInfo);
            Assert.Equal(typeof(string), property.ParameterType);
            Assert.Equal(BindingSource.Query, property.BindingInfo.BindingSource);
        }
        private bool IsActionAttributeRouted(ActionModel action)
        {
            foreach (var controllerSelectorModel in action.Controller.Selectors)
            {
                if (controllerSelectorModel.AttributeRouteModel?.Template != null)
                {
                    return true;
                }
            }

            foreach (var actionSelectorModel in action.Selectors)
            {
                if (actionSelectorModel.AttributeRouteModel?.Template != null)
                {
                    return true;
                }
            }

            return false;
        }
Пример #9
0
        public ActionModel(ActionModel other)
        {
            if (other == null)
            {
                throw new ArgumentNullException(nameof(other));
            }

            ActionMethod = other.ActionMethod;
            ActionName = other.ActionName;

            // Not making a deep copy of the controller, this action still belongs to the same controller.
            Controller = other.Controller;

            // These are just metadata, safe to create new collections
            Attributes = new List<object>(other.Attributes);
            Filters = new List<IFilterMetadata>(other.Filters);
            Properties = new Dictionary<object, object>(other.Properties);
            RouteValues = new Dictionary<string, string>(other.RouteValues, StringComparer.OrdinalIgnoreCase);

            // Make a deep copy of other 'model' types.
            ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
            Parameters = new List<ParameterModel>(other.Parameters.Select(p => new ParameterModel(p)));
            Selectors = new List<SelectorModel>(other.Selectors.Select(s => new SelectorModel(s)));
        }
Пример #10
0
 public void Apply(ActionModel model)
 {
     model.Properties["description"] = _value;
 }
Пример #11
0
        public void CopyConstructor_CopiesAllProperties()
        {
            // Arrange
            var action = new ActionModel(
                typeof(TestController).GetMethod("Edit"),
                new List<object>()
                {
                    new HttpGetAttribute(),
                    new MyFilterAttribute(),
                });

            var selectorModel = new SelectorModel();
            selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
            action.Selectors.Add(selectorModel);
            action.ActionName = "Edit";

            action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
                                                    new List<object>());
            action.Filters.Add(new MyFilterAttribute());
            action.RouteConstraints.Add(new MyRouteConstraintAttribute());
            action.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));

            // Act
            var action2 = new ActionModel(action);

            // Assert
            foreach (var property in typeof(ActionModel).GetProperties())
            {
                // Reflection is used to make sure the test fails when a new property is added.
                if (property.Name.Equals("ApiExplorer") ||
                    property.Name.Equals("Selectors") ||
                    property.Name.Equals("Parameters"))
                {
                    // This test excludes other ApplicationModel objects on purpose because we deep copy them.
                    continue;
                }

                var value1 = property.GetValue(action);
                var value2 = property.GetValue(action2);

                if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IEnumerable<object>)value1);
                }
                else if (typeof(IDictionary<object, object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IDictionary<object, object>)value1);
                }
                else if (property.PropertyType.GetTypeInfo().IsValueType ||
                    Nullable.GetUnderlyingType(property.PropertyType) != null)
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
                }
                else
                {
                    Assert.Same(value1, value2);

                    // Ensure non-default value
                    Assert.NotNull(value1);
                }
            }
        }
Пример #12
0
 public static T GetProperty <T>(this ActionModel action)
 public void Apply(ActionModel action)
 {
     action.ActionName = "ChangedAction";
 }
Пример #14
0
        public void CopyConstructor_CopiesAllProperties()
        {
            // Arrange
            var action = new ActionModel(
                typeof(TestController).GetMethod("Edit"),
                new List <object>()
            {
                new HttpGetAttribute(),
                new MyFilterAttribute(),
            });

            var selectorModel = new SelectorModel();

            selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
            action.Selectors.Add(selectorModel);
            action.ActionName = "Edit";

            action.Controller = new ControllerModel
                                    (typeof(TestController).GetTypeInfo(),
                                    new List <object>());
            action.Filters.Add(new MyFilterAttribute());
            action.RouteValues.Add("key", "value");
            action.Properties.Add(new KeyValuePair <object, object>("test key", "test value"));

            // Act
            var action2 = new ActionModel(action);

            // Assert
            foreach (var property in typeof(ActionModel).GetProperties())
            {
                // Reflection is used to make sure the test fails when a new property is added.
                if (property.Name.Equals("ApiExplorer") ||
                    property.Name.Equals("Selectors") ||
                    property.Name.Equals("Parameters"))
                {
                    // This test excludes other ApplicationModel objects on purpose because we deep copy them.
                    continue;
                }

                var value1 = property.GetValue(action);
                var value2 = property.GetValue(action2);

                if (typeof(IEnumerable <object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal <object>((IEnumerable <object>)value1, (IEnumerable <object>)value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IEnumerable <object>)value1);
                }
                else if (typeof(IDictionary <string, string>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IDictionary <string, string>)value1);
                }
                else if (typeof(IDictionary <object, object>).IsAssignableFrom(property.PropertyType))
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEmpty((IDictionary <object, object>)value1);
                }
                else if (property.PropertyType.GetTypeInfo().IsValueType ||
                         Nullable.GetUnderlyingType(property.PropertyType) != null)
                {
                    Assert.Equal(value1, value2);

                    // Ensure non-default value
                    Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
                }
                else if (property.Name.Equals(nameof(ActionModel.DisplayName)))
                {
                    // DisplayName is re-calculated, hence reference equality wouldn't work.
                    Assert.Equal(value1, value2);
                }
                else
                {
                    Assert.Same(value1, value2);

                    // Ensure non-default value
                    Assert.NotNull(value1);
                }
            }
        }
Пример #15
0
 /// <summary>
 /// Determines if this instance of <see cref="IActionModelConvention"/> applies to a specified <paramref name="action"/>.
 /// </summary>
 /// <param name="action">The <see cref="ActionModel"/>.</param>
 /// <returns>
 /// <see langword="true"/> if the convention applies, otherwise <see langword="false"/>.
 /// Derived types may override this method to selectively apply this convention.
 /// </returns>
 protected virtual bool ShouldApply(ActionModel action) => true;
Пример #16
0
 private static TValue GetProperty <TValue>(ActionModel action)
 {
     return(Assert.IsType <TValue>(action.Properties[typeof(TValue)]));
 }
 private static void NormalizeSelectorRoutes(string moduleName, string controllerName, ActionModel action)
 {
     foreach (var selector in action.Selectors)
     {
         if (selector.AttributeRouteModel == null)
         {
             selector.AttributeRouteModel = CreateAbpServiceAttributeRouteModel(
                 moduleName,
                 controllerName,
                 action
             );
         }
     }
 }
        private void ConfigureSelector(string moduleName, string controllerName, ActionModel action, [CanBeNull] AbpServiceControllerSetting configuration)
        {
            RemoveEmptySelectors(action.Selectors);

            if (!action.Selectors.Any())
            {
                AddAbpServiceSelector(moduleName, controllerName, action, configuration);
            }
            else
            {
                NormalizeSelectorRoutes(moduleName, controllerName, action);
            }
        }
        private static bool CanUseFormBodyBinding(ActionModel action)
        {
            foreach (var selector in action.Selectors)
            {
                if (selector.ActionConstraints == null)
                {
                    continue;
                }

                foreach (var actionConstraint in selector.ActionConstraints)
                {
                    var httpMethodActionConstraint = actionConstraint as HttpMethodActionConstraint;
                    if (httpMethodActionConstraint == null)
                    {
                        continue;
                    }

                    if (httpMethodActionConstraint.HttpMethods.All(hm => hm.IsIn("GET", "DELETE", "TRACE", "HEAD")))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
Пример #20
0
 public void Apply(ActionModel model)
 {
     model.ActionName = _actionName;
 }
        private void SetHttpMethodFromConvention(ActionModel action)
        {
            foreach (var selector in action.Selectors)
            {
                if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Count() > 0)
                {
                    // If the HttpMethods are set from attributes, don't override it with the convention
                    return;
                }
            }

            // The Method name is used to infer verb constraints. Changing the action name has no impact.
            foreach (var verb in SupportedHttpMethodConventions)
            {
                if (action.ActionMethod.Name.StartsWith(verb, StringComparison.OrdinalIgnoreCase))
                {
                    foreach (var selector in action.Selectors)
                    {
                        selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));
                    }

                    return;
                }
            }

            // If no convention matches, then assume POST
            foreach (var actionSelectorModel in action.Selectors)
            {
                actionSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { "POST" }));
            }
        }
 private void ConfigureApiExplorer(ActionModel action)
 {
     if (action.ApiExplorer.IsVisible == null)
     {
         var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(action.ActionMethod);
         action.ApiExplorer.IsVisible = remoteServiceAtt?.IsEnabledFor(action.ActionMethod);
     }
 }
        private bool CanUseFormBodyBinding(ActionModel action, ParameterModel parameter)
        {
            if (_configuration.Value.FormBodyBindingIgnoredTypes.Any(t => t.IsAssignableFrom(parameter.ParameterInfo.ParameterType)))
            {
                return false;
            }

            foreach (var selector in action.Selectors)
            {
                if (selector.ActionConstraints == null)
                {
                    continue;
                }

                foreach (var actionConstraint in selector.ActionConstraints)
                {
                    var httpMethodActionConstraint = actionConstraint as HttpMethodActionConstraint;
                    if (httpMethodActionConstraint == null)
                    {
                        continue;
                    }

                    if (httpMethodActionConstraint.HttpMethods.All(hm => hm.IsIn("GET", "DELETE", "TRACE", "HEAD")))
                    {
                        return false;
                    }
                }
            }

            return true;
        }
        private void AddAbpServiceSelector(string moduleName, string controllerName, ActionModel action, [CanBeNull] AbpServiceControllerSetting configuration)
        {
            var abpServiceSelectorModel = new SelectorModel
            {
                AttributeRouteModel = CreateAbpServiceAttributeRouteModel(moduleName, controllerName, action)
            };

            var verb = configuration?.UseConventionalHttpVerbs == true
                           ? ProxyScriptingHelper.GetConventionalVerbForMethodName(action.ActionName)
                           : ProxyScriptingHelper.DefaultHttpVerb;

            abpServiceSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));

            action.Selectors.Add(abpServiceSelectorModel);
        }
 public void Apply(ActionModel action)
 {
     action.Properties.Add("TestProperty", "TestValue");
 }
 private static AttributeRouteModel CreateAbpServiceAttributeRouteModel(string moduleName, string controllerName, ActionModel action)
 {
     return new AttributeRouteModel(
         new RouteAttribute(
             $"api/services/{moduleName}/{controllerName}/{action.ActionName}"
         )
     );
 }
        /// <summary>
        /// Creates the <see cref="ActionModel"/> instance for the given action <see cref="MethodInfo"/>.
        /// </summary>
        /// <param name="typeInfo">The controller <see cref="TypeInfo"/>.</param>
        /// <param name="methodInfo">The action <see cref="MethodInfo"/>.</param>
        /// <returns>
        /// An <see cref="ActionModel"/> instance for the given action <see cref="MethodInfo"/> or
        /// <c>null</c> if the <paramref name="methodInfo"/> does not represent an action.
        /// </returns>
        internal ActionModel?CreateActionModel(
            TypeInfo typeInfo,
            MethodInfo methodInfo)
        {
            if (typeInfo == null)
            {
                throw new ArgumentNullException(nameof(typeInfo));
            }

            if (methodInfo == null)
            {
                throw new ArgumentNullException(nameof(methodInfo));
            }

            if (!IsAction(typeInfo, methodInfo))
            {
                return(null);
            }

            var attributes = methodInfo.GetCustomAttributes(inherit: true);

            var actionModel = new ActionModel(methodInfo, attributes);

            AddRange(actionModel.Filters, attributes.OfType <IFilterMetadata>());

            var actionName = attributes.OfType <ActionNameAttribute>().FirstOrDefault();

            if (actionName?.Name != null)
            {
                actionModel.ActionName = actionName.Name;
            }
            else
            {
                actionModel.ActionName = CanonicalizeActionName(methodInfo.Name);
            }

            var apiVisibility = attributes.OfType <IApiDescriptionVisibilityProvider>().FirstOrDefault();

            if (apiVisibility != null)
            {
                actionModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
            }

            var apiGroupName = attributes.OfType <IApiDescriptionGroupNameProvider>().FirstOrDefault();

            if (apiGroupName != null)
            {
                actionModel.ApiExplorer.GroupName = apiGroupName.GroupName;
            }

            foreach (var routeValueProvider in attributes.OfType <IRouteValueProvider>())
            {
                actionModel.RouteValues.Add(routeValueProvider.RouteKey, routeValueProvider.RouteValue);
            }

            // Now we need to determine the action selection info (cross-section of routes and constraints)
            //
            // For attribute routes on a action, we want to support 'overriding' routes on a
            // virtual method, but allow 'overriding'. So we need to walk up the hierarchy looking
            // for the first definition to define routes.
            //
            // Then we want to 'filter' the set of attributes, so that only the effective routes apply.
            var currentMethodInfo = methodInfo;

            IRouteTemplateProvider[] routeAttributes;

            while (true)
            {
                routeAttributes = currentMethodInfo
                                  .GetCustomAttributes(inherit: false)
                                  .OfType <IRouteTemplateProvider>()
                                  .ToArray();

                if (routeAttributes.Length > 0)
                {
                    // Found 1 or more route attributes.
                    break;
                }

                // GetBaseDefinition returns 'this' when it gets to the bottom of the chain.
                var nextMethodInfo = currentMethodInfo.GetBaseDefinition();
                if (currentMethodInfo == nextMethodInfo)
                {
                    break;
                }

                currentMethodInfo = nextMethodInfo;
            }

            // This is fairly complicated so that we maintain referential equality between items in
            // ActionModel.Attributes and ActionModel.Attributes[*].Attribute.
            var applicableAttributes = new List <object>(routeAttributes.Length);

            foreach (var attribute in attributes)
            {
                if (attribute is IRouteTemplateProvider)
                {
                    // This attribute is a route-attribute, leave it out.
                }
                else
                {
                    applicableAttributes.Add(attribute);
                }
            }

            applicableAttributes.AddRange(routeAttributes);
            AddRange(actionModel.Selectors, CreateSelectors(applicableAttributes));

            return(actionModel);
        }