public static IRouteInformation CompleteBodyRequiredContent(
            IRouteInformation info,
            ControllerActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
            {
                return(info);
            }

            // avoid get and delete methods
            if (info.HttpMethod == "GET" || info.HttpMethod == "DELETE")
            {
                return(info);
            }

            var methodInfo       = actionDescriptor.MethodInfo;
            var methodParams     = methodInfo.GetParameters().ToList();
            var routeConstraints = RoutingScraper.GetRouteConstraints(info).ToList();

            // first check if there is any param marked as body to give it more priority even if they are not the first param
            foreach (var param in methodParams)
            {
                // check if param has FromQuery or FromBody Attribute to avoid them
                if (param.ContainsAttribute <FromBodyAttribute>() && !routeConstraints.Contains(param.Name))
                {
                    info.BodyParams[param.Name] = param.ParameterType;
                    break;
                }
            }

            // only 1 will be supported for now
            if (info.BodyParams.Any())
            {
                return(info);
            }

            // now check if there is params not marked as body who does not belong to the route constraint
            // part of the route.
            // this is done because Asp.Net Core support getting object with HttPost without marking them with [FromBody]
            foreach (var param in methodParams)
            {
                // it will take the first one always
                if (!routeConstraints.Contains(param.Name))
                {
                    info.BodyParams[param.Name] = param.ParameterType;
                    break;
                }
            }

            return(info);
        }
        private IEnumerable <IRouteInformation> GetEndpointsCandidates()
        {
            var routes = _actionDescriptorCollectionProvider.ActionDescriptors.Items;

            foreach (var action in routes)
            {
                //  Check if the action needs to be ignored
                if (CheckIfRouteShouldBeIgnored(action))
                {
                    continue;
                }

                var info = new RouteInformation
                {
                    RouteTemplate = action.AttributeRouteInfo?.Template
                };

                // Path and Invocation of Razor Pages
                if (action is PageActionDescriptor pageAction)
                {
                    info.Path = pageAction.ViewEnginePath;
                }

                // Path of Route Attribute
                if (action.AttributeRouteInfo != null)
                {
                    info.Path = $"/{action.AttributeRouteInfo.Template}";
                }

                // Path and Invocation of Controller/Action
                if (action is ControllerActionDescriptor actionDescriptor)
                {
                    if (string.IsNullOrWhiteSpace(info.Path))
                    {
                        info.Path = $"/{actionDescriptor.ControllerName}/{actionDescriptor.ActionName}";
                    }
                }

                // Extract HTTP Verb
                if (action.ActionConstraints != null)
                {
                    var constraintTypes = action.ActionConstraints.Select(t => t.GetType()).ToList();

                    // Check Http method constraint
                    if (constraintTypes.Contains(typeof(HttpMethodActionConstraint)))
                    {
                        var httpVerbConstrain =
                            action.ActionConstraints.FirstOrDefault(a =>
                                                                    a.GetType() == typeof(HttpMethodActionConstraint));

                        if (httpVerbConstrain is HttpMethodActionConstraint httpMethodAction)
                        {
                            info.HttpMethod = string.Join(",", httpMethodAction.HttpMethods);
                        }
                    }
                }

                //  when need to update this after the http completion
                if (action is ControllerActionDescriptor controllerAction)
                {
                    RoutingScraper.CompleteRoutingInformation(info, controllerAction);
                    BodyContentScraper.CompleteBodyRequiredContent(info, controllerAction);
                    QueryStringScraper.CompleteQueryStringRequiredParams(info, controllerAction);
                }

                yield return(info);
            }
        }
        public static IRouteInformation CompleteQueryStringRequiredParams(
            IRouteInformation info,
            ControllerActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
            {
                return(info);
            }

            // find all params who don't belong to the route template and are not nullable
            var methodInfo       = actionDescriptor.MethodInfo;
            var methodParams     = methodInfo.GetParameters().ToList();
            var routeConstraints = RoutingScraper.GetRouteConstraints(info).ToList();

            bool IsAlreadyIncluded(string name)
            {
                // save them all who are not body params or constraints with or without FromQuery
                return(routeConstraints.Contains(name) ||
                       info.BodyParams.Any(b => b.Key == name) ||
                       info.QueryParams.Any(b => b.Key == name));
            }

            bool IsSupported(Type requestedType)
            {
                // check if the type is one of the supported for querystring
                var notNumericSupportedQueryStringTypes = new List <Type>
                {
                    typeof(string),
                    typeof(char),
                    typeof(DateTime),
                    typeof(bool)
                };

                return(notNumericSupportedQueryStringTypes.Contains(requestedType) || requestedType.IsNumericType());
            }

            foreach (var param in methodParams)
            {
                // ignore existing ones
                if (IsAlreadyIncluded(param.Name))
                {
                    continue;
                }

                var supportedType = IsSupported(param.ParameterType);
                if (supportedType)
                {
                    info.QueryParams[param.Name] = param.ParameterType;
                }
                else if (param.ParameterType.IsClass && !param.ParameterType.IsAbstract &&
                         param.ParameterType.SupportParameterLessConstructor() &&
                         param.ContainsAttribute <FromQueryAttribute>())
                {
                    // this is a complex object marked with [FromQuery]
                    // asp.net core let our get complex objects in get http fashion without specify one by one the params by using a complex class
                    // and mark it with [FromQuery]
                    var getObjectProperties = param.ParameterType
                                              .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                              .Where(o => o.CanWrite && o.CanRead)
                                              .ToList();

                    foreach (var property in getObjectProperties)
                    {
                        // ignore existing ones
                        if (IsAlreadyIncluded(property.Name))
                        {
                            continue;
                        }

                        // we don't need recursivity for complex type as asp.net core does not support it
                        if (!IsSupported(property.PropertyType))
                        {
                            continue;
                        }

                        info.QueryParams[property.Name] = property.PropertyType;
                    }
                }
            }

            return(info);
        }