using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging; namespace zero.Routing; public class ZeroRoutesTransformer : DynamicRouteValueTransformer { readonly IRouteResolver RouteResolver; readonly ILogger Logger; public ZeroRoutesTransformer(IRouteResolver routeResolver, ILogger logger) { RouteResolver = routeResolver; Logger = logger; } public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) { if (State == null || State is not ZeroRoutesTransformerType) { throw new ArgumentNullException(nameof(State), "Pass a ZeroRoutesTransformerType instance to the state parameter in the dynamic route transformer"); } ZeroRoutesTransformerType type = (ZeroRoutesTransformerType)State; IRouteEndpoint endpoint = httpContext.Features.Get(); IRouteModel route = httpContext.Features.Get(); // check if a resolved endpoint routes to a Razor Page. // if the Page transformer can't find an endpoint it exits as the Controller transformer already ran before. if (type == ZeroRoutesTransformerType.Page) { if (endpoint is PageRouteEndpoint) { return Apply(httpContext, values, endpoint, type); } return null; } // a route was already set, therefore we prevent another execution if (route != null) { return null; } // resolve route from URL route = await RouteResolver.ResolveUrl(httpContext); if (route == null) { return null; } httpContext.Features.Set(route); // map the route to an endpoint // an endpoint can be a controller action or a Razor page. // in case it is a Razor page we exit here and let the transformer run again within the page context (MapDynamicPageRoute). endpoint = RouteResolver.MapEndpoint(route); if (endpoint == null) { return null; } httpContext.Features.Set(endpoint); if (endpoint is PageRouteEndpoint) { return null; } return Apply(httpContext, values, endpoint, type); } protected virtual RouteValueDictionary Apply(HttpContext httpContext, RouteValueDictionary values, IRouteEndpoint endpoint, ZeroRoutesTransformerType type) { endpoint.Apply(values); HashSet logValues = new(); foreach ((string key, object value) in values) { if (key != "url" && value != null && (value is not string || ((string)value).HasValue())) { logValues.Add(key + ": " + value); } } Logger.LogDebug("Routed {url} to [{values}] (transformer: {type})", httpContext.Request.Path, logValues, type); return values; } } public enum ZeroRoutesTransformerType { Controller = 0, Page = 1 }