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);
        }
        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);
        }