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