dynamic route transformer now works for both controllers + pages
This commit is contained in:
@@ -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>
|
||||
///
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user