.NET Core 6 MVC中CMS动态路由实现方案咨询
.NET Core 6 MVC类CMS网站动态路由实现方案
需求回顾
需要构建前后端均为.NET Core 6 MVC的类CMS系统,实现以下动态路由能力:
- 后端创建/更新页面时,前端站点自动注册对应路由
- 前端视图可通过路由名称生成页面链接
- 页面Slug修改时,前端路由可实时更新
可行方案
方案1:自定义路由约束+缓存映射
这是最轻量化的实现方式,无需复杂的路由扩展:
- 注册通用基础路由
在前端站点的Program.cs中注册一个匹配任意Slug的路由:app.MapControllerRoute( name: "cms-page", pattern: "{*slug}", defaults: new { controller = "Page", action = "Detail" } ); - 实现自定义路由约束
创建实现IRouteConstraint的约束类,在匹配逻辑中从缓存读取页面Slug与PageId的映射关系,验证请求Slug是否有效,并注入PageId到路由值:
然后修改基础路由,添加约束:public class CmsPageRouteConstraint : IRouteConstraint { private readonly IMemoryCache _cache; private readonly IPageService _pageService; private const string CacheKey = "CmsPageSlugMap"; public CmsPageRouteConstraint(IMemoryCache cache, IPageService pageService) { _cache = cache; _pageService = pageService; } public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { if (!values.TryGetValue("slug", out var slugValue) || slugValue is not string slug) return false; var slugMap = _cache.Get<Dictionary<string, int>>(CacheKey) ?? RefreshSlugMap(); if (slugMap.TryGetValue(slug, out var pageId)) { values["id"] = pageId; return true; } return false; } private Dictionary<string, int> RefreshSlugMap() { var pages = _pageService.GetAll(); var slugMap = pages.ToDictionary(p => p.Url, p => p.PageId); _cache.Set(CacheKey, slugMap, TimeSpan.FromMinutes(5)); return slugMap; } }app.MapControllerRoute( name: "cms-page", pattern: "{*slug:cmsPage}", defaults: new { controller = "Page", action = "Detail" } ); // 注册约束 services.Configure<RouteOptions>(options => { options.ConstraintMap.Add("cmsPage", typeof(CmsPageRouteConstraint)); }); - 动态更新路由
后端修改页面Slug时,调用前端站点的API接口,触发缓存刷新:// 前端站点的刷新接口示例 [HttpPost("refresh-slug-map")] public IActionResult RefreshSlugMap() { _cache.Remove("CmsPageSlugMap"); return Ok(); } - 生成页面链接
在Razor视图中,直接通过Slug生成链接:@foreach (CmsPage page in Pages) { <a href="@Url.Action("Detail", "Page", new { slug = page.Url })">@page.Title</a> }
方案2:自定义动态路由提供程序
适合需要保留原ASP.NET MVC中命名路由特性的场景:
- 实现IDynamicRouteProvider
引用Microsoft.AspNetCore.Mvc.Routing包,实现动态路由提供程序,从数据库读取页面信息注册路由:public class CmsDynamicRouteProvider : IDynamicRouteProvider { private readonly IPageService _pageService; public CmsDynamicRouteProvider(IPageService pageService) { _pageService = pageService; } public void PopulateDynamicRoutes(DynamicRouteProviderContext context) { var pages = _pageService.GetAll(); foreach (var page in pages) { context.Routes.Add(new Route( new MvcRouteHandler(), page.Url, new RouteValueDictionary(), new RouteValueDictionary(), new RouteValueDictionary(new { controller = "Page", action = "Detail", id = page.PageId }), null, $"page-{page.PageId}" )); } } } - 注册服务
在Program.cs中注册该提供程序:services.AddSingleton<IDynamicRouteProvider, CmsDynamicRouteProvider>(); - 动态更新路由
可以通过后台定时任务刷新路由,或者在后端更新页面时,通过消息队列通知前端站点重新加载路由:// 示例:通过IHostApplicationLifetime触发路由刷新 public async Task RefreshRoutes(IHostApplicationLifetime lifetime) { // 可结合轻量配置刷新逻辑,避免重启站点 lifetime.ApplicationStopping.Reset(); // 重新加载路由逻辑 } - 生成页面链接
沿用原ASP.NET MVC的方式,通过路由名称生成链接:@foreach (CmsPage page in Pages) { <a href="@Url.RouteUrl($"page-{page.PageId}")">@page.Title</a> }
方案3:自定义EndpointDataSource
适合需要完全控制端点生命周期的场景:
- 实现自定义EndpointDataSource
该类负责从数据库读取页面信息生成Endpoint,并支持动态刷新:public class CmsEndpointDataSource : EndpointDataSource { private readonly IPageService _pageService; private List<Endpoint> _endpoints = new(); private readonly object _lock = new(); public CmsEndpointDataSource(IPageService pageService) { _pageService = pageService; RefreshEndpoints(); } public void RefreshEndpoints() { lock (_lock) { var pages = _pageService.GetAll(); _endpoints = pages.Select(page => { var routePattern = RoutePatternFactory.Parse(page.Url); var endpoint = new RouteEndpoint( context => { context.Request.RouteValues["controller"] = "Page"; context.Request.RouteValues["action"] = "Detail"; context.Request.RouteValues["id"] = page.PageId; return Task.CompletedTask; }, routePattern, 0, new EndpointMetadataCollection(new MvcActionEndpointMetadata()), $"page-{page.PageId}" ); return endpoint; }).ToList(); } NotifyChanged(); } public override IReadOnlyList<Endpoint> Endpoints => _endpoints; } - 注册数据源
在Program.cs中注册为单例:services.AddSingleton<EndpointDataSource, CmsEndpointDataSource>(); - 动态更新路由
后端修改页面Slug时,调用前端站点的API触发RefreshEndpoints()方法,路由系统会自动感知端点变化:[HttpPost("refresh-endpoints")] public IActionResult RefreshEndpoints([FromServices] CmsEndpointDataSource dataSource) { dataSource.RefreshEndpoints(); return Ok(); } - 生成页面链接
同样支持通过路由名称生成链接,与方案2一致。
内容的提问来源于stack exchange,提问作者El MoZo
相关产品推荐
相关产品推荐

