namespace Kit.Helpers.Routes { using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using System; using System.Reflection; public class ControllerMethod { public string Name { get; } public string Controller { get; } public string RouteTemplate { get; } public string Method => _route.GetMethod(); public MethodInfo MethodInfo { get; } public RouteValueDictionary RouteDataTokens { get { var result = new RouteValueDictionary(); _route.Route.DataTokens?.ForEach(x => result.Add(x.Key, x.Value)); return result; } } private RouteFluent _route { get; } public ControllerMethod(string name, MethodInfo methodInfo, RouteFluent route) { MethodInfo = methodInfo; _route = route; Name = name; Controller = _route.Route?.Defaults["controller"]?.ToString() ?? string.Empty; RouteTemplate = _route.Route?.RouteTemplate ?? string.Empty; } } public static class RouteHelper_Extensions { public static List ControllerMethods = new List(); private static IInlineConstraintResolver CreateInlineConstraintResolver(IServiceProvider serviceProvider) { var inlineConstraintResolver = serviceProvider .GetRequiredService(); var parameterPolicyFactory = serviceProvider .GetRequiredService(); // This inline constraint resolver will return a null constraint for non-IRouteConstraint // parameter policies so Route does not error return new BackCompatInlineConstraintResolver(inlineConstraintResolver, parameterPolicyFactory); } internal class NullRouteConstraint : IRouteConstraint { public static readonly NullRouteConstraint Instance = new NullRouteConstraint(); private NullRouteConstraint() { } public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { return true; } } private class BackCompatInlineConstraintResolver : IInlineConstraintResolver { private readonly IInlineConstraintResolver _inner; private readonly ParameterPolicyFactory _parameterPolicyFactory; public BackCompatInlineConstraintResolver(IInlineConstraintResolver inner, ParameterPolicyFactory parameterPolicyFactory) { _inner = inner; _parameterPolicyFactory = parameterPolicyFactory; } public IRouteConstraint ResolveConstraint(string inlineConstraint) { var routeConstraint = _inner.ResolveConstraint(inlineConstraint); if (routeConstraint != null) { return routeConstraint; } var parameterPolicy = _parameterPolicyFactory.Create(null, inlineConstraint); if (parameterPolicy != null) { // Logic inside Route will skip adding NullRouteConstraint return NullRouteConstraint.Instance; } return null; } } public static RouteFluent AddRoute(this IRouteBuilder routeBuilder, string name, string url, string controller, string action, MethodInfo methodInfo) { var inline = CreateInlineConstraintResolver(routeBuilder.ServiceProvider); var route = new Route(routeBuilder.DefaultHandler, name, url, new RouteValueDictionary(new { controller = controller, action = action }), null, null, inline); routeBuilder.Routes.Add(route); var result = new RouteFluent { Route = route }; ControllerMethods.Add(new ControllerMethod(name, methodInfo, result)); return result; } public static ControllerBaseRouteFluent AddControllerBase(this IRouteBuilder routeTable) where TController : ControllerBase { return new ControllerBaseRouteFluent(routeTable); } public static ControllerRouteFluent AddController(this IRouteBuilder routeTable) where TController : Controller { return new ControllerRouteFluent(routeTable); } public static RouteBase GetBaseRoute(this ControllerContext context) { return context.GetBaseRoute(); } public static bool GetForbidForReadonly(this RouteData route) { return (route.DataTokens["forbidForReadonly"] as bool?) ?? false; } } }