.NET Core 5 中如何动态更新路由表实现CMS自定义内容页路由
解决方案
.NET 5 已经移除了静态的RouteTable类,要实现动态内容路由,推荐以下两种实现方式:
方案一:动态路由匹配(优先推荐)
该方案不需要手动维护路由表,仅通过通配符路由匹配数据库记录,修改页面URL时只需更新数据库即可,实现成本极低,适合绝大多数CMS场景。
- 注册通配符路由,放在所有路由规则的最后:
app.UseEndpoints(endpoints => { // 原有路由规则,比如后台路由、默认控制器路由等 endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // CMS内容页通配符路由,优先级最低,仅匹配未被其他路由命中的请求 endpoints.MapControllerRoute( name: "cms-page", pattern: "{*url}", defaults: new { controller = "Home", action = "GetPage" }); });
- 改造控制器的页面处理逻辑:
public ActionResult GetPage(string url) { // 处理首页空路径的情况 url ??= string.Empty; // 根据请求路径查询对应内容页 var page = pageService.GetPageByUrl(url); if (page == null) { return NotFound(); } return View("index", page); }
如果担心数据库查询性能,可以将URL和页面对应关系存入内存缓存,修改页面URL时同步更新缓存即可。
方案二:动态更新路由表
如果需要完全对齐旧框架的路由注册逻辑,每个页面单独注册路由,可以通过.NET Core的IActionDescriptorChangeProvider实现路由的动态重载:
- 实现路由变更通知类
public class CmsRouteChangeProvider : IActionDescriptorChangeProvider { private CancellationTokenSource _cts = new(); public IChangeToken GetChangeToken() { return new CancellationChangeToken(_cts.Token); } public void NotifyRouteChanged() { var oldCts = Interlocked.Exchange(ref _cts, new CancellationTokenSource()); oldCts.Cancel(); oldCts.Dispose(); } }
- 注册服务到容器
// Startup.cs ConfigureServices 中添加 services.AddSingleton<CmsRouteChangeProvider>(); services.AddSingleton<IActionDescriptorChangeProvider>(sp => sp.GetRequiredService<CmsRouteChangeProvider>());
- 实现动态路由数据源
public class CmsEndpointDataSource : EndpointDataSource { private readonly IPageService _pageService; private readonly ActionDescriptor _getPageAction; public CmsEndpointDataSource(IPageService pageService, IActionDescriptorCollectionProvider actionProvider) { _pageService = pageService; // 预获取GetPage方法的Action描述符 _getPageAction = actionProvider.ActionDescriptors.Items .First(a => a.RouteValues["controller"] == "Home" && a.RouteValues["action"] == "GetPage"); } public override IReadOnlyList<Endpoint> Endpoints { get { var endpoints = new List<Endpoint>(); var allPages = _pageService.GetAllPages(); foreach (var page in allPages) { var routePattern = RoutePatternFactory.Parse(page.Url.TrimStart('/')); var builder = new RouteEndpointBuilder( async context => { context.Request.RouteValues["guid"] = page.Id.ToString(); var invoker = context.RequestServices.GetRequiredService<IActionInvokerFactory>() .CreateInvoker(new ActionContext(context, new RouteData(), _getPageAction)); await invoker.InvokeAsync(); }, routePattern, 1 ); builder.Metadata.AddRange(_getPageAction.EndpointMetadata); endpoints.Add(builder.Build()); } return endpoints; } } public override IChangeToken GetChangeToken() { return _getPageAction.GetChangeToken(); } }
注册该数据源:
services.AddSingleton<EndpointDataSource, CmsEndpointDataSource>();
- 页面URL更新后触发路由重载
// 页面URL更新的业务逻辑中 public void UpdatePageUrl(Guid pageId, string newUrl) { // 更新数据库记录 _pageService.UpdatePage(pageId, new { Url = newUrl }); // 触发路由重载 _routeChangeProvider.NotifyRouteChanged(); }
调用NotifyRouteChanged后,框架会自动重新加载所有路由规则,实现路由表的动态更新。
内容的提问来源于stack exchange,提问作者Bimmie
相关产品推荐
相关产品推荐

