dynamic route transformer now works for both controllers + pages

This commit is contained in:
2022-01-07 12:47:37 +01:00
parent 88e42f17b1
commit ee7682505c
10 changed files with 131 additions and 45 deletions
@@ -7,11 +7,11 @@ public class RoutingEndpointOptions
class ResolverMap
{
public Type Type { get; set; }
public Func<IRouteModel, RouteEndpoint> Impl { get; set; }
public Func<IRouteModel, IRouteEndpoint> Impl { get; set; }
}
public void Add<T>(Func<T, RouteEndpoint> resolver) where T : class, IRouteModel
public void Add<T>(Func<T, IRouteEndpoint> resolver) where T : class, IRouteModel
{
Resolvers.Add(new()
{
@@ -20,7 +20,7 @@ public class RoutingEndpointOptions
});
}
public void Replace<T>(Func<T, RouteEndpoint> resolver) where T : class, IRouteModel
public void Replace<T>(Func<T, IRouteEndpoint> resolver) where T : class, IRouteModel
{
Remove<T>();
Add(resolver);
@@ -32,17 +32,17 @@ public class RoutingEndpointOptions
Resolvers.RemoveWhere(x => x.Type == type);
}
public Func<IRouteModel, RouteEndpoint> Get<T>() where T : IRouteModel => Get(typeof(T));
public Func<IRouteModel, IRouteEndpoint> Get<T>() where T : IRouteModel => Get(typeof(T));
public IEnumerable<Func<IRouteModel, RouteEndpoint>> GetAll<T>() where T : IRouteModel => GetAll(typeof(T));
public IEnumerable<Func<IRouteModel, IRouteEndpoint>> GetAll<T>() where T : IRouteModel => GetAll(typeof(T));
public Func<IRouteModel, RouteEndpoint> Get(Type type)
public Func<IRouteModel, IRouteEndpoint> Get(Type type)
{
ResolverMap map = Resolvers.LastOrDefault(x => x.Type == type);
return map?.Impl;
}
public IEnumerable<Func<IRouteModel, RouteEndpoint>> GetAll(Type type)
public IEnumerable<Func<IRouteModel, IRouteEndpoint>> GetAll(Type type)
{
IEnumerable<ResolverMap> maps = Resolvers.Where(x => x.Type == type);
return maps.Select(map => map.Impl).Where(x => x != null);
@@ -5,7 +5,7 @@ public class RoutingOptions
public RoutingOptions()
{
PageRouteIdBuilder = new PageRouteIdBuilder();
DefaultEndpoint = new("ZeroFrontend", "Index");
DefaultEndpoint = new ControllerRouteEndpoint("ZeroFrontend", "Index");
EndpointResolvers = new();
PageResolvers = new();
//ErrorReexecutionPath = "/error";
@@ -16,10 +16,10 @@ public class RoutingOptions
public IPageRouteIdBuilder PageRouteIdBuilder { get; set; }
public RouteEndpoint NotFoundEndpoint { get; set; }
public IRouteEndpoint NotFoundEndpoint { get; set; }
public RouteEndpoint DefaultEndpoint { get; set; }
public IRouteEndpoint DefaultEndpoint { get; set; }
/// <summary>
///
+35 -11
View File
@@ -1,23 +1,47 @@
namespace zero.Routing;
using Microsoft.AspNetCore.Routing;
public class RouteEndpoint
namespace zero.Routing;
public class ControllerRouteEndpoint : IRouteEndpoint
{
public RouteEndpoint(string controller, string action = "Index")
public string Controller { get; set; }
public string Action { get; set; }
public ControllerRouteEndpoint(string controller, string action = "Index")
{
Type = RouteEndpointType.Controller;
Controller = controller;
Action = action;
}
public RouteEndpointType Type { get; set; }
public string Controller { get; set; }
public string Action { get; set; }
public virtual void Apply(RouteValueDictionary values)
{
values["controller"] = Controller;
values["action"] = Action;
}
}
public enum RouteEndpointType
public class PageRouteEndpoint : IRouteEndpoint
{
Controller = 0
public string Page { get; set; }
public PageRouteEndpoint(string page)
{
Page = page;
}
public virtual void Apply(RouteValueDictionary values)
{
values["page"] = Page;
}
}
public interface IRouteEndpoint
{
/// <summary>
/// Append values to the RouteValueDictionary
/// </summary>
void Apply(RouteValueDictionary values);
}
+4 -4
View File
@@ -22,7 +22,7 @@ public class RouteResolver : Routes, IRouteResolver
/// <inheritdoc />
public async Task<IRouteModel> ResolveUrl(HttpContext context, Application application, string path)
{
IZeroDocumentSession session = Store.Session();
IZeroDocumentSession session = Store.Session(application.Database);
path = path.Length > 1 ? path.TrimEnd(PATH_SEPERATOR) : path;
@@ -98,7 +98,7 @@ public class RouteResolver : Routes, IRouteResolver
return null;
}
Application app = await AppResolver.ResolveFromRequest(context);
Application app = await AppResolver.Resolve(context, Context.BackofficeUser);
string path = context.Request.Path;
if (app == null)
@@ -111,7 +111,7 @@ public class RouteResolver : Routes, IRouteResolver
/// <inheritdoc />
public RouteEndpoint MapEndpoint(IRouteModel route)
public IRouteEndpoint MapEndpoint(IRouteModel route)
{
if (TryGetProvider(route.Route.ProviderAlias, out IRouteProvider provider))
{
@@ -152,5 +152,5 @@ public interface IRouteResolver
/// <summary>
/// Get endpoint the route maps to
/// </summary>
RouteEndpoint MapEndpoint(IRouteModel route);
IRouteEndpoint MapEndpoint(IRouteModel route);
}
@@ -23,7 +23,8 @@ public static class ZeroEndpointRouteBuilderExtensions
public static IZeroEndpointRouteBuilder MapZeroRoutes(this IZeroEndpointRouteBuilder endpoints)
{
endpoints.MapDynamicControllerRoute<ZeroRoutesTransformer>("/{**url}", state: null, order: 10);
endpoints.MapDynamicControllerRoute<ZeroRoutesTransformer>("/{**url}", state: ZeroRoutesTransformerType.Controller, order: 10);
endpoints.MapDynamicPageRoute<ZeroRoutesTransformer>("/{**url}", state: ZeroRoutesTransformerType.Page, order: 11);
return endpoints;
}
}
+5 -5
View File
@@ -107,15 +107,15 @@ public abstract class ZeroRouteProvider : IRouteProvider
public virtual string Id(ISupportsRouting model) => "routes." + Alias + "." + Key(model);
/// <inheritdoc />
public virtual RouteEndpoint Map(RoutingContext context, IRouteModel route)
public virtual IRouteEndpoint Map(RoutingContext context, IRouteModel route)
{
RoutingOptions options = context.Context.Options.For<RoutingOptions>();
IEnumerable<Func<IRouteModel, RouteEndpoint>> resolvers = options.EndpointResolvers.GetAll(route.GetType());
IEnumerable<Func<IRouteModel, IRouteEndpoint>> resolvers = options.EndpointResolvers.GetAll(route.GetType());
foreach (Func<IRouteModel, RouteEndpoint> resolver in resolvers.Reverse())
foreach (Func<IRouteModel, IRouteEndpoint> resolver in resolvers.Reverse())
{
RouteEndpoint endpoint = resolver(route);
IRouteEndpoint endpoint = resolver(route);
if (endpoint != null)
{
@@ -232,7 +232,7 @@ public interface IRouteProvider
/// <summary>
/// Map a route model to an endpoint
/// </summary>
RouteEndpoint Map(RoutingContext context, IRouteModel route);
IRouteEndpoint Map(RoutingContext context, IRouteModel route);
/// <summary>
/// Find a persisted route for an entity
+72 -12
View File
@@ -1,28 +1,53 @@
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<ZeroRoutesTransformer> Logger;
public ZeroRoutesTransformer(IRouteResolver routeResolver)
public ZeroRoutesTransformer(IRouteResolver routeResolver, ILogger<ZeroRoutesTransformer> logger)
{
RouteResolver = routeResolver;
}
Logger = logger;
}
public override async ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
IRouteModel route = httpContext.Features.Get<IRouteModel>();
if (route != null)
{
return null;
}
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<IRouteEndpoint>();
IRouteModel route = httpContext.Features.Get<IRouteModel>();
// 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)
@@ -30,17 +55,52 @@ public class ZeroRoutesTransformer : DynamicRouteValueTransformer
return null;
}
httpContext.Features.Set<IRouteModel>(route);
httpContext.Features.Set(route);
RouteEndpoint endpoint = RouteResolver.MapEndpoint(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;
}
values["controller"] = endpoint.Controller;
values["action"] = endpoint.Action;
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<string> 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
}
+1 -1
View File
@@ -18,7 +18,7 @@ internal class ZeroRoutingModule : ZeroModule
services.AddScoped<ILinks, Links>();
services.AddScoped<ILinkProvider, PageLinkProvider>();
services.AddScoped<ILinkProvider, RawLinkProvider>();
services.AddScoped<ZeroRoutesTransformer>();
services.AddTransient<ZeroRoutesTransformer>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<MatcherPolicy, NotFoundSelectorPolicy>());
services.AddScoped<IInterceptor, ZeroEntityRouteInterceptor>();
+1 -1
View File
@@ -14,8 +14,8 @@ public class ZeroApplicationBuilder : IZeroApplicationBuilder
App = app;
App.UseStaticFiles();
//App.UseExceptionHandler("/zero/api/error");
App.UseRouting();
App.UseMiddleware<ZeroContextMiddleware>();
App.UseRouting();
App.UseAuthentication();
App.UseAuthorization();
+1
View File
@@ -31,6 +31,7 @@ public class ZeroBuilder
setupAction?.Invoke(StartupOptions);
services.AddControllers();
services.AddRazorPages();
// adds and discovers additional and built-in assemblies
new AssemblyDiscovery(Mvc).Execute(StartupOptions.AssemblyDiscoveryRules);