コード例 #1
0
        private ControllerActionDescriptor CreateActionDescriptor(
            string httpMethod, string routeTemplate, string actionFixtureName)
        {
            var descriptor = new ControllerActionDescriptor();
            descriptor.SetProperty(new ApiDescriptionActionData());
            descriptor.DisplayName = actionFixtureName;

            descriptor.ActionConstraints = new List<IActionConstraintMetadata>
            {
                new HttpMethodConstraint(new[] { httpMethod })
            };
            descriptor.AttributeRouteInfo = new AttributeRouteInfo { Template = routeTemplate };

            descriptor.MethodInfo = typeof(ActionFixtures).GetMethod(actionFixtureName);
            if (descriptor.MethodInfo == null)
                throw new InvalidOperationException(
                    string.Format("{0} is not declared in ActionFixtures", actionFixtureName));

            descriptor.Parameters = descriptor.MethodInfo.GetParameters()
                .Select(paramInfo => new ParameterDescriptor
                    {
                        Name = paramInfo.Name,
                        ParameterType = paramInfo.ParameterType,
                        BindingInfo = BindingInfo.GetBindingInfo(paramInfo.GetCustomAttributes(false))
                    })
                .ToList();

            // Set some additional properties - typically done via IApplicationModelConvention
            var attributes = descriptor.MethodInfo.GetCustomAttributes(true);
            descriptor.Properties.Add("ActionAttributes", attributes);
            descriptor.Properties.Add("IsObsolete", attributes.OfType<ObsoleteAttribute>().Any());

            return descriptor;
        }
コード例 #2
0
        private static void AddProperties(
            ControllerActionDescriptor actionDescriptor,
            ActionModel action,
            ControllerModel controller,
            ApplicationModel application)
        {
            foreach (var item in application.Properties)
            {
                actionDescriptor.Properties[item.Key] = item.Value;
            }

            foreach (var item in controller.Properties)
            {
                actionDescriptor.Properties[item.Key] = item.Value;
            }

            foreach (var item in action.Properties)
            {
                actionDescriptor.Properties[item.Key] = item.Value;
            }
        }
コード例 #3
0
        private static void ReplaceAttributeRouteTokens(
            ControllerActionDescriptor actionDescriptor,
            IList <string> routeTemplateErrors)
        {
            try
            {
                actionDescriptor.AttributeRouteInfo.Template = AttributeRouteModel.ReplaceTokens(
                    actionDescriptor.AttributeRouteInfo.Template,
                    actionDescriptor.RouteValueDefaults);
            }
            catch (InvalidOperationException ex)
            {
                // Routing will throw an InvalidOperationException here if we can't parse/replace tokens
                // in the template.
                var message = Resources.FormatAttributeRoute_IndividualErrorMessage(
                    actionDescriptor.DisplayName,
                    Environment.NewLine,
                    ex.Message);

                routeTemplateErrors.Add(message);
            }
        }
コード例 #4
0
        public static void AddRouteConstraints(
            ControllerActionDescriptor actionDescriptor,
            ControllerModel controller,
            ActionModel action)
        {
            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",
                                                          string.Empty));
            }
        }
コード例 #5
0
        private static void AddApiExplorerInfo(
            ControllerActionDescriptor actionDescriptor,
            ActionModel action,
            ControllerModel controller)
        {
            var apiExplorerIsVisible = action.ApiExplorer?.IsVisible ?? controller.ApiExplorer?.IsVisible ?? false;

            if (apiExplorerIsVisible)
            {
                if (!IsAttributeRoutedAction(actionDescriptor))
                {
                    // ApiExplorer is only supported on attribute routed actions.
                    throw new InvalidOperationException(Resources.FormatApiExplorer_UnsupportedAction(
                                                            actionDescriptor.DisplayName));
                }

                var apiExplorerActionData = new ApiDescriptionActionData()
                {
                    GroupName = action.ApiExplorer?.GroupName ?? controller.ApiExplorer?.GroupName,
                };

                actionDescriptor.SetProperty(apiExplorerActionData);
            }
        }
コード例 #6
0
 private static bool IsAttributeRoutedAction(ControllerActionDescriptor actionDescriptor)
 {
     return(actionDescriptor.AttributeRouteInfo?.Template != null);
 }
コード例 #7
0
        public static void AddRouteConstraints(
            ISet <string> removalConstraints,
            ControllerActionDescriptor actionDescriptor,
            ControllerModel controller,
            ActionModel action)
        {
            // Apply all the constraints defined on the action, then controller (for example, [Area])
            // to the actions. Also keep track of all the constraints that require preventing actions
            // without the constraint to match. For example, actions without an [Area] attribute on their
            // controller should not match when a value has been given for area when matching a url or
            // generating a link.
            foreach (var constraintAttribute in action.RouteConstraints)
            {
                if (constraintAttribute.BlockNonAttributedActions)
                {
                    removalConstraints.Add(constraintAttribute.RouteKey);
                }

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

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

                // Skip duplicates - this also means that a value on the action will take precedence
                if (!HasConstraint(actionDescriptor.RouteConstraints, constraintAttribute.RouteKey))
                {
                    if (constraintAttribute.RouteKeyHandling == RouteKeyHandling.CatchAll)
                    {
                        actionDescriptor.RouteConstraints.Add(
                            RouteDataActionConstraint.CreateCatchAll(
                                constraintAttribute.RouteKey));
                    }
                    else
                    {
                        actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                                  constraintAttribute.RouteKey,
                                                                  constraintAttribute.RouteValue));
                    }
                }
            }

            // Lastly add the 'default' values
            if (!HasConstraint(actionDescriptor.RouteConstraints, "action"))
            {
                actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                          "action",
                                                          action.ActionName ?? string.Empty));
            }

            if (!HasConstraint(actionDescriptor.RouteConstraints, "controller"))
            {
                actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(
                                                          "controller",
                                                          controller.ControllerName));
            }
        }
コード例 #8
0
 public TestControllerActionInvoker(
     ActionContext actionContext,
     IFilterProvider[] filterProvider,
     MockControllerFactory controllerFactory,
     ControllerActionDescriptor descriptor,
     IReadOnlyList<IInputFormatter> inputFormatters,
     IReadOnlyList<IOutputFormatter> outputFormatters,
     IControllerActionArgumentBinder controllerActionArgumentBinder,
     IReadOnlyList<IModelBinder> modelBinders,
     IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
     IReadOnlyList<IValueProviderFactory> valueProviderFactories,
     IScopedInstance<ActionBindingContext> actionBindingContext,
     ITempDataDictionary tempData,
     ILoggerFactory loggerFactory,
     int maxAllowedErrorsInModelState)
     : base(
           actionContext,
           filterProvider,
           controllerFactory,
           descriptor,
           inputFormatters,
           outputFormatters,
           controllerActionArgumentBinder,
           modelBinders,
           modelValidatorProviders,
           valueProviderFactories,
           actionBindingContext,
           tempData,
           loggerFactory,
           maxAllowedErrorsInModelState)
 {
     ControllerFactory = controllerFactory;
 }
コード例 #9
0
        public async Task Invoke_UsesDefaultValuesIfNotBound()
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
                BoundProperties = new List<ParameterDescriptor>(),
                MethodInfo = typeof(TestController).GetTypeInfo()
                    .DeclaredMethods
                    .First(m => m.Name.Equals("ActionMethodWithDefaultValues", StringComparison.Ordinal)),

                Parameters = new List<ParameterDescriptor>
                {
                    new ParameterDescriptor
                    {
                        Name = "value",
                        ParameterType = typeof(int),
                        BindingInfo = new BindingInfo(),
                    }
                },
                FilterDescriptors = new List<FilterDescriptor>()
            };

            var binder = new Mock<IModelBinder>();
            binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
                  .Returns(Task.FromResult<ModelBindingResult>(result: null));
            var context = new Mock<HttpContext>();
            context.SetupGet(c => c.Items)
                   .Returns(new Dictionary<object, object>());
            context.Setup(c => c.RequestServices.GetService(typeof(ITempDataDictionary)))
                       .Returns(new Mock<ITempDataDictionary>().Object);
            context.Setup(c => c.RequestServices.GetService(typeof(ILoggerFactory)))
                       .Returns(new NullLoggerFactory());

            var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor);

            var controllerFactory = new Mock<IControllerFactory>();
            controllerFactory.Setup(c => c.CreateController(It.IsAny<ActionContext>()))
                             .Returns(new TestController());

            var metadataProvider = new EmptyModelMetadataProvider();
            var invoker = new ControllerActionInvoker(
                actionContext,
                new List<IFilterProvider>(),
                controllerFactory.Object,
                actionDescriptor,
                new IInputFormatter[0],
                new IOutputFormatter[0],
                new DefaultControllerActionArgumentBinder(
                    metadataProvider,
                    new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider)),
                new IModelBinder[] { binder.Object },
                new IModelValidatorProvider[0],
                new IValueProviderFactory[0],
                new MockScopedInstance<ActionBindingContext>(),
                Mock.Of<ITempDataDictionary>(),
                new NullLoggerFactory(),
                200);

            // Act
            await invoker.InvokeAsync();

            // Assert
            Assert.Equal(5, context.Object.Items["Result"]);
        }
コード例 #10
0
        private TestControllerActionInvoker CreateInvoker(
            IFilter[] filters,
            bool actionThrows = false,
            ITempDataDictionary tempData = null,
            int maxAllowedErrorsInModelState = 200)
        {
            var actionDescriptor = new ControllerActionDescriptor()
            {
                FilterDescriptors = new List<FilterDescriptor>(),
                Parameters = new List<ParameterDescriptor>(),
            };

            if (actionThrows)
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ThrowingActionMethod");
            }
            else
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ActionMethod");
            }

            tempData = tempData ?? new Mock<ITempDataDictionary>().Object;
            var httpContext = new Mock<HttpContext>(MockBehavior.Loose);
            var httpRequest = new DefaultHttpContext().Request;
            var httpResponse = new DefaultHttpContext().Response;

            httpContext.SetupGet(c => c.Request).Returns(httpRequest);
            httpContext.SetupGet(c => c.Response).Returns(httpResponse);
            httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary)))
                       .Returns(tempData);
            httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
                       .Returns(new Mock<ILogger<ObjectResult>>().Object);

            httpResponse.Body = new MemoryStream();

            var formatter = new Mock<IOutputFormatter>();
            formatter
                .Setup(f => f.CanWriteResult(It.IsAny<OutputFormatterContext>(), It.IsAny<MediaTypeHeaderValue>()))
                .Returns(true);

            formatter
                .Setup(f => f.WriteAsync(It.IsAny<OutputFormatterContext>()))
                .Returns<OutputFormatterContext>(async c =>
                {
                    await c.HttpContext.Response.WriteAsync(c.Object.ToString());
                });

            var options = new MvcOptions();
            options.OutputFormatters.Add(formatter.Object);

            var optionsAccessor = new Mock<IOptions<MvcOptions>>();
            optionsAccessor
                .SetupGet(o => o.Options)
                .Returns(options);

            httpContext
                .Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
                .Returns(optionsAccessor.Object);

            var actionContext = new ActionContext(
                httpContext: httpContext.Object,
                routeData: new RouteData(),
                actionDescriptor: actionDescriptor);

            var filterProvider = new Mock<IFilterProvider>(MockBehavior.Strict);
            filterProvider
                .Setup(fp => fp.OnProvidersExecuting(It.IsAny<FilterProviderContext>()))
                .Callback<FilterProviderContext>(context =>
                    {
                        foreach (var filter in filters.Select(f => new FilterItem(null, f)))
                        {
                            context.Results.Add(filter);
                        }
                    });

            filterProvider.Setup(fp => fp.OnProvidersExecuted(It.IsAny<FilterProviderContext>()))
                          .Verifiable();

            filterProvider.SetupGet(fp => fp.Order)
                          .Returns(DefaultOrder.DefaultFrameworkSortOrder);

            var invoker = new TestControllerActionInvoker(
                actionContext,
                new[] { filterProvider.Object },
                new MockControllerFactory(this),
                actionDescriptor,
                new IInputFormatter[0],
                new IOutputFormatter[0],
                Mock.Of<IControllerActionArgumentBinder>(),
                new IModelBinder[0],
                new IModelValidatorProvider[0],
                new IValueProviderFactory[0],
                new MockScopedInstance<ActionBindingContext>(),
                tempData,
                new NullLoggerFactory(),
                maxAllowedErrorsInModelState);

            return invoker;
        }
コード例 #11
0
 public TestControllerActionInvoker(
     ActionContext actionContext,
     IFilterProvider[] filterProvider,
     MockControllerFactory controllerFactory,
     ControllerActionDescriptor descriptor,
     IInputFormattersProvider inputFormattersProvider,
     IControllerActionArgumentBinder controllerActionArgumentBinder,
     IModelBinderProvider modelBinderProvider,
     IModelValidatorProviderProvider modelValidatorProviderProvider,
     IValueProviderFactoryProvider valueProviderFactoryProvider,
     IScopedInstance<ActionBindingContext> actionBindingContext,
     ITempDataDictionary tempData)
     : base(
           actionContext,
           filterProvider,
           controllerFactory,
           descriptor,
           inputFormattersProvider,
           controllerActionArgumentBinder,
           modelBinderProvider,
           modelValidatorProviderProvider,
           valueProviderFactoryProvider,
           actionBindingContext,
           tempData)
 {
     ControllerFactory = controllerFactory;
 }
コード例 #12
0
        private TestControllerActionInvoker CreateInvoker(
            IFilter[] filters,
            bool actionThrows = false,
            ITempDataDictionary tempData = null)
        {
            var actionDescriptor = new ControllerActionDescriptor()
            {
                FilterDescriptors = new List<FilterDescriptor>(),
                Parameters = new List<ParameterDescriptor>(),
            };

            if (actionThrows)
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ThrowingActionMethod");
            }
            else
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ActionMethod");
            }

            tempData = tempData ?? new Mock<ITempDataDictionary>().Object;
            var httpContext = new Mock<HttpContext>(MockBehavior.Loose);
            var httpRequest = new DefaultHttpContext().Request;
            var httpResponse = new DefaultHttpContext().Response;
            var mockFormattersProvider = new Mock<IOutputFormattersProvider>();
            mockFormattersProvider.SetupGet(o => o.OutputFormatters)
                                  .Returns(
                                        new List<IOutputFormatter>()
                                        {
                                            new JsonOutputFormatter()
                                        });
            httpContext.SetupGet(c => c.Request).Returns(httpRequest);
            httpContext.SetupGet(c => c.Response).Returns(httpResponse);
            httpContext.Setup(o => o.RequestServices.GetService(typeof(IOutputFormattersProvider)))
                       .Returns(mockFormattersProvider.Object);
            httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary)))
                       .Returns(tempData);
            httpResponse.Body = new MemoryStream();

            var options = new Mock<IOptions<MvcOptions>>();
            options.SetupGet(o => o.Options)
                       .Returns(new MvcOptions());
            httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
                       .Returns(options.Object);

            var actionContext = new ActionContext(
                httpContext: httpContext.Object,
                routeData: new RouteData(),
                actionDescriptor: actionDescriptor);

            var filterProvider = new Mock<IFilterProvider>(MockBehavior.Strict);
            filterProvider
                .Setup(fp => fp.OnProvidersExecuting(It.IsAny<FilterProviderContext>()))
                .Callback<FilterProviderContext>(context =>
                    {
                        foreach (var filter in filters.Select(f => new FilterItem(null, f)))
                        {
                            context.Results.Add(filter);
                        }
                    });

            filterProvider.Setup(fp => fp.OnProvidersExecuted(It.IsAny<FilterProviderContext>()))
                          .Verifiable();

            filterProvider.SetupGet(fp => fp.Order)
                          .Returns(DefaultOrder.DefaultFrameworkSortOrder);


            var inputFormattersProvider = new Mock<IInputFormattersProvider>();
            inputFormattersProvider.SetupGet(o => o.InputFormatters)
                                            .Returns(new List<IInputFormatter>());
            var excludeFilterProvider = new Mock<IValidationExcludeFiltersProvider>();
            excludeFilterProvider.SetupGet(o => o.ExcludeFilters)
                                 .Returns(new List<IExcludeTypeValidationFilter>());
            var invoker = new TestControllerActionInvoker(
                actionContext,
                new[] { filterProvider.Object },
                new MockControllerFactory(this),
                actionDescriptor,
                inputFormattersProvider.Object,
                Mock.Of<IControllerActionArgumentBinder>(),
                new MockModelBinderProvider(),
                new MockModelValidatorProviderProvider(),
                new MockValueProviderFactoryProvider(),
                new MockScopedInstance<ActionBindingContext>(),
                tempData);

            return invoker;
        }
コード例 #13
0
 public TestControllerActionInvoker(
     ActionContext actionContext,
     IFilterProvider[] filterProvider,
     MockControllerFactory controllerFactory,
     ControllerActionDescriptor descriptor,
     IReadOnlyList<IInputFormatter> inputFormatters,
     IReadOnlyList<IOutputFormatter> outputFormatters,
     IControllerActionArgumentBinder controllerActionArgumentBinder,
     IReadOnlyList<IModelBinder> modelBinders,
     IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
     IReadOnlyList<IValueProviderFactory> valueProviderFactories,
     IActionBindingContextAccessor actionBindingContext,
     ILogger logger,
     INotifier notifier,
     int maxAllowedErrorsInModelState)
     : base(
           actionContext,
           filterProvider,
           controllerFactory,
           descriptor,
           inputFormatters,
           outputFormatters,
           controllerActionArgumentBinder,
           modelBinders,
           modelValidatorProviders,
           valueProviderFactories,
           actionBindingContext,
           logger,
           notifier,
           maxAllowedErrorsInModelState)
 {
     ControllerFactory = controllerFactory;
 }
コード例 #14
0
        private void ValidateActionGroupConfiguration(
            IDictionary <MethodInfo, IDictionary <ActionModel, IList <ControllerActionDescriptor> > > methodMap,
            ControllerActionDescriptor actionDescriptor,
            IDictionary <MethodInfo, string> routingConfigurationErrors)
        {
            string combinedErrorMessage = null;

            var hasAttributeRoutedActions      = false;
            var hasConventionallyRoutedActions = false;

            var invalidHttpMethodActions = new Dictionary <ActionModel, IEnumerable <string> >();

            var actionsForMethod = methodMap[actionDescriptor.MethodInfo];

            foreach (var reflectedAction in actionsForMethod)
            {
                foreach (var action in reflectedAction.Value)
                {
                    if (IsAttributeRoutedAction(action))
                    {
                        hasAttributeRoutedActions = true;
                    }
                    else
                    {
                        hasConventionallyRoutedActions = true;
                    }
                }

                // Keep a list of actions with possible invalid IHttpActionMethodProvider attributes
                // to generate an error in case the method generates attribute routed actions.
                ValidateActionHttpMethodProviders(reflectedAction.Key, invalidHttpMethodActions);
            }

            // Validate that no method result in attribute and non attribute actions at the same time.
            // By design, mixing attribute and conventionally actions in the same method is not allowed.
            // This is for example the case when someone uses[HttpGet("Products")] and[HttpPost]
            // on the same  method.
            if (hasAttributeRoutedActions && hasConventionallyRoutedActions)
            {
                combinedErrorMessage = CreateMixedRoutedActionDescriptorsErrorMessage(
                    actionDescriptor,
                    actionsForMethod);
            }

            // Validate that no method that creates attribute routed actions and
            // also uses attributes that only constrain the set of HTTP methods. For example,
            // if an attribute that implements IActionHttpMethodProvider but does not implement
            // IRouteTemplateProvider is used with an attribute that implements IRouteTemplateProvider on
            // the same action, the HTTP methods provided by the attribute that only implements
            // IActionHttpMethodProvider would be silently ignored, so we choose to throw to
            // inform the user of the invalid configuration.
            if (hasAttributeRoutedActions && invalidHttpMethodActions.Any())
            {
                var errorMessage = CreateInvalidActionHttpMethodProviderErrorMessage(
                    actionDescriptor,
                    invalidHttpMethodActions,
                    actionsForMethod);

                combinedErrorMessage = CombineErrorMessage(combinedErrorMessage, errorMessage);
            }

            if (combinedErrorMessage != null)
            {
                routingConfigurationErrors.Add(actionDescriptor.MethodInfo, combinedErrorMessage);
            }
        }