private IList <ApiParameterDescription> GetParameters(ApiParameterContext context)
        {
            // First, get parameters from the model-binding/parameter-binding side of the world.
            if (context.ActionDescriptor.Parameters != null)
            {
                foreach (var actionParameter in context.ActionDescriptor.Parameters)
                {
                    var visitor = new PseudoModelBindingVisitor(context, actionParameter);

                    ModelMetadata metadata = null;
                    if (_mvcOptions.AllowValidatingTopLevelNodes &&
                        actionParameter is ControllerParameterDescriptor controllerParameterDescriptor &&
                        _modelMetadataProvider is ModelMetadataProvider provider)
                    {
                        // The default model metadata provider derives from ModelMetadataProvider
                        // and can therefore supply information about attributes applied to parameters.
                        metadata = provider.GetMetadataForParameter(controllerParameterDescriptor.ParameterInfo);
                    }
                    else
                    {
                        // For backward compatibility, if there's a custom model metadata provider that
                        // only implements the older IModelMetadataProvider interface, access the more
                        // limited metadata information it supplies. In this scenario, validation attributes
                        // are not supported on parameters.
                        metadata = _modelMetadataProvider.GetMetadataForType(actionParameter.ParameterType);
                    }

                    var bindingContext = ApiParameterDescriptionContext.GetContext(
                        metadata,
                        actionParameter.BindingInfo,
                        propertyName: actionParameter.Name);
                    visitor.WalkParameter(bindingContext);
                }
Beispiel #2
0
        ApiParameterDescription CreateResult(ApiParameterDescriptionContext bindingContext, BindingSource source, string containerName)
        {
            var modelMetadata = bindingContext.ModelMetadata;
            var type          = modelMetadata.ModelType;

            if (type.IsODataActionParameters() && Context.RouteContext.Operation?.IsAction() == true)
            {
                var action     = (IEdmAction)Context.RouteContext.Operation;
                var apiVersion = Context.RouteContext.ApiVersion;

                type = Context.TypeBuilder.NewActionParameters(Context.Services, action, apiVersion, Context.RouteContext.ActionDescriptor.ControllerName);
            }
            else
            {
                type = type.SubstituteIfNecessary(new TypeSubstitutionContext(Context.Services, Context.TypeBuilder));
            }

            return(new ApiParameterDescription()
            {
                ModelMetadata = modelMetadata.SubstituteIfNecessary(type),
                Name = GetName(containerName, bindingContext),
                Source = source,
                Type = type,
                ParameterDescriptor = Parameter,
            });
        }
            public void WalkParameter(ApiParameterDescriptionContext context)
            {
                // Attempt to find a binding source for the parameter
                //
                // The default is ModelBinding (aka all default value providers)
                var source = BindingSource.ModelBinding;

                Visit(context, source, containerName: string.Empty);
            }
Beispiel #4
0
        private static string GetName(string containerName, ApiParameterDescriptionContext metadata)
        {
            if (string.IsNullOrEmpty(metadata.BinderModelName))
            {
                return(ModelNames.CreatePropertyModelName(containerName, metadata.PropertyName));
            }

            return(metadata.BinderModelName);
        }
 private static string GetName(string containerName, ApiParameterDescriptionContext metadata)
 {
     if (!string.IsNullOrEmpty(metadata.BinderModelName))
     {
         // Name was explicitly provided
         return(metadata.BinderModelName);
     }
     else
     {
         return(ModelNames.CreatePropertyModelName(containerName, metadata.PropertyName));
     }
 }
Beispiel #6
0
 private ApiParameterDescription CreateResult(
     ApiParameterDescriptionContext bindingContext,
     BindingSource source,
     string containerName)
 {
     return(new ApiParameterDescription()
     {
         ModelMetadata = bindingContext.ModelMetadata,
         Name = GetName(containerName, bindingContext),
         Source = source,
         Type = bindingContext.ModelMetadata.ModelType,
     });
 }
Beispiel #7
0
        void Visit(ApiParameterDescriptionContext bindingContext, BindingSource ambientSource, string containerName)
        {
            var source = bindingContext.BindingSource;

            if (source != null && source.IsGreedy)
            {
                Context.Results.Add(CreateResult(bindingContext, source, containerName));
                return;
            }

            var modelMetadata = bindingContext.ModelMetadata;

            if (modelMetadata.IsEnumerableType || !modelMetadata.IsComplexType || modelMetadata.Properties.Count == 0)
            {
                Context.Results.Add(CreateResult(bindingContext, source ?? ambientSource, containerName));
                return;
            }

            var newContainerName = containerName;

            if (modelMetadata.ContainerType != null)
            {
                newContainerName = GetName(containerName, bindingContext);
            }

            source ??= ambientSource;

            for (var i = 0; i < modelMetadata.Properties.Count; i++)
            {
                var propertyMetadata = modelMetadata.Properties[i];
                var key             = new PropertyKey(propertyMetadata, source);
                var propertyContext = new ApiParameterDescriptionContext(propertyMetadata, bindingInfo: null, propertyName: null);

                if (Visited.Add(key))
                {
                    Visit(propertyContext, source, newContainerName);
                }
                else
                {
                    Context.Results.Add(CreateResult(propertyContext, source, newContainerName));
                }
            }
        }
            private void Visit(
                ApiParameterDescriptionContext bindingContext,
                BindingSource ambientSource,
                string containerName)
            {
                var source = bindingContext.BindingSource;

                if (source != null && source.IsGreedy)
                {
                    // We have a definite answer for this model. This is a greedy source like
                    // [FromBody] so there's no need to consider properties.
                    Context.Results.Add(CreateResult(bindingContext, source, containerName));
                    return;
                }

                var modelMetadata = bindingContext.ModelMetadata;

                // For any property which is a leaf node, we don't want to keep traversing:
                //
                //  1)  Collections - while it's possible to have binder attributes on the inside of a collection,
                //      it hardly seems useful, and would result in some very wierd binding.
                //
                //  2)  Simple Types - These are generally part of the .net framework - primitives, or types which have a
                //      type converter from string.
                //
                //  3)  Types with no properties. Obviously nothing to explore there.
                //
                if (modelMetadata.IsEnumerableType ||
                    !modelMetadata.IsComplexType ||
                    modelMetadata.Properties.Count == 0)
                {
                    Context.Results.Add(CreateResult(bindingContext, source ?? ambientSource, containerName));
                    return;
                }

                // This will come from composite model binding - so investigate what's going on with each property.
                //
                // Ex:
                //
                //      public IActionResult PlaceOrder(OrderDTO order) {...}
                //
                //      public class OrderDTO
                //      {
                //          public int AccountId { get; set; }
                //
                //          [FromBody]
                //          public Order { get; set; }
                //      }
                //
                // This should result in two parameters:
                //
                //  AccountId - source: Any
                //  Order - source: Body
                //

                // We don't want to append the **parameter** name when building a model name.
                var newContainerName = containerName;

                if (modelMetadata.ContainerType != null)
                {
                    newContainerName = GetName(containerName, bindingContext);
                }

                for (var i = 0; i < modelMetadata.Properties.Count; i++)
                {
                    var propertyMetadata = modelMetadata.Properties[i];
                    var key             = new PropertyKey(propertyMetadata, source);
                    var propertyContext = ApiParameterDescriptionContext.GetContext(
                        propertyMetadata,
                        bindingInfo: null,
                        propertyName: null);

                    if (Visited.Add(key))
                    {
                        Visit(propertyContext, source ?? ambientSource, newContainerName);
                    }
                    else
                    {
                        // This is cycle, so just add a result rather than traversing.
                        Context.Results.Add(CreateResult(propertyContext, source ?? ambientSource, newContainerName));
                    }
                }
            }
        private IList <ApiParameterDescription> GetParameters(ApiParameterContext context)
        {
            // First, get parameters from the model-binding/parameter-binding side of the world.
            if (context.ActionDescriptor.Parameters != null)
            {
                foreach (var actionParameter in context.ActionDescriptor.Parameters)
                {
                    var visitor  = new PseudoModelBindingVisitor(context, actionParameter);
                    var metadata = _modelMetadataProvider.GetMetadataForType(actionParameter.ParameterType);

                    var bindingContext = ApiParameterDescriptionContext.GetContext(
                        metadata,
                        actionParameter.BindingInfo,
                        propertyName: actionParameter.Name);
                    visitor.WalkParameter(bindingContext);
                }
            }

            if (context.ActionDescriptor.BoundProperties != null)
            {
                foreach (var actionParameter in context.ActionDescriptor.BoundProperties)
                {
                    var visitor       = new PseudoModelBindingVisitor(context, actionParameter);
                    var modelMetadata = context.MetadataProvider.GetMetadataForProperty(
                        containerType: context.ActionDescriptor.ControllerTypeInfo.AsType(),
                        propertyName: actionParameter.Name);

                    var bindingContext = ApiParameterDescriptionContext.GetContext(
                        modelMetadata,
                        actionParameter.BindingInfo,
                        propertyName: actionParameter.Name);

                    visitor.WalkParameter(bindingContext);
                }
            }

            for (var i = context.Results.Count - 1; i >= 0; i--)
            {
                // Remove any 'hidden' parameters. These are things that can't come from user input,
                // so they aren't worth showing.
                if (!context.Results[i].Source.IsFromRequest)
                {
                    context.Results.RemoveAt(i);
                }
            }

            // Next, we want to join up any route parameters with those discovered from the action's parameters.
            var routeParameters = new Dictionary <string, ApiParameterRouteInfo>(StringComparer.OrdinalIgnoreCase);

            foreach (var routeParameter in context.RouteParameters)
            {
                routeParameters.Add(routeParameter.Name, CreateRouteInfo(routeParameter));
            }

            foreach (var parameter in context.Results)
            {
                if (parameter.Source == BindingSource.Path ||
                    parameter.Source == BindingSource.ModelBinding ||
                    parameter.Source == BindingSource.Custom)
                {
                    ApiParameterRouteInfo routeInfo;
                    if (routeParameters.TryGetValue(parameter.Name, out routeInfo))
                    {
                        parameter.RouteInfo = routeInfo;
                        routeParameters.Remove(parameter.Name);

                        if (parameter.Source == BindingSource.ModelBinding &&
                            !parameter.RouteInfo.IsOptional)
                        {
                            // If we didn't see any information about the parameter, but we have
                            // a route parameter that matches, let's switch it to path.
                            parameter.Source = BindingSource.Path;
                        }
                    }
                }
            }

            // Lastly, create a parameter representation for each route parameter that did not find
            // a partner.
            foreach (var routeParameter in routeParameters)
            {
                context.Results.Add(new ApiParameterDescription()
                {
                    Name      = routeParameter.Key,
                    RouteInfo = routeParameter.Value,
                    Source    = BindingSource.Path,
                });
            }

            return(context.Results);
        }
Beispiel #10
0
 internal void WalkParameter(ApiParameterDescriptionContext context) => Visit(context, BindingSource.ModelBinding, containerName: string.Empty);