如何通过Application Parts复用页面与控制器并配置独立子路由?
复用外部程序集资源并隔离路由的实现方案
核心思路
通过自定义路由约定给外部程序集的控制器、Razor Pages统一添加/legacy路由前缀,同时单独映射其wwwroot资源,彻底隔离原有应用与外部资源的命名冲突。
1. 控制器路由隔离
给外部程序集的所有控制器强制加上/legacy前缀,避免与原有控制器路由冲突。
自定义控制器路由约定
public class LegacyControllerRouteConvention : IApplicationModelConvention { private readonly string _legacyAssemblyName; private readonly string _routePrefix; public LegacyControllerRouteConvention(string legacyAssemblyName, string routePrefix) { _legacyAssemblyName = legacyAssemblyName; _routePrefix = routePrefix.StartsWith("/") ? routePrefix : $"/{routePrefix}"; } public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { // 仅处理目标程序集的控制器 if (controller.ControllerType.Assembly.GetName().Name == _legacyAssemblyName) { // 清除原有路由规则,避免冲突 controller.Selectors.Clear(); // 添加带前缀的新路由 controller.Selectors.Add(new SelectorModel { AttributeRouteModel = new AttributeRouteModel { Template = $"{_routePrefix}/[controller]/[action]" } }); } } } }
在Program.cs中注册
// 加载外部程序集(替换为你的Legacy控制器所在类型) var legacyAssembly = typeof(LegacyProject.HomeController).Assembly; builder.Services.AddControllersWithViews() .AddApplicationPart(legacyAssembly) // 引入外部程序集 .AddMvcOptions(options => { // 添加路由约定 options.Conventions.Add(new LegacyControllerRouteConvention(legacyAssembly.GetName().Name, "legacy")); });
2. Razor Pages路由隔离
同理,给外部程序集的Razor Pages统一添加/legacy前缀,并处理布局冲突。
自定义Page路由约定
public class LegacyPageRouteConvention : IPageRouteModelConvention { private readonly string _legacyAssemblyName; private readonly string _routePrefix; public LegacyPageRouteConvention(string legacyAssemblyName, string routePrefix) { _legacyAssemblyName = legacyAssemblyName; _routePrefix = routePrefix.StartsWith("/") ? routePrefix : $"/{routePrefix}"; } public void Apply(PageRouteModel model) { if (model.PageType.Assembly.GetName().Name == _legacyAssemblyName) { // 修改路由前缀 var originalTemplate = model.Selectors[0].AttributeRouteModel.Template; model.Selectors[0].AttributeRouteModel.Template = $"{_legacyAssemblyName}/{originalTemplate}"; // 强制使用外部程序集的专属布局,解决_Layout命名冲突 model.ViewData["Layout"] = "/Legacy/Shared/_Layout.cshtml"; } } }
在Program.cs中注册
builder.Services.AddRazorPages() .AddApplicationPart(legacyAssembly) .AddRazorPagesOptions(options => { options.Conventions.Add(new LegacyPageRouteConvention(legacyAssembly.GetName().Name, "legacy")); // 可选:设置legacy页面的默认路由(访问/legacy时指向外部程序集的Index页) options.Conventions.AddPageRoute("/Legacy/Index", "legacy"); // 添加视图查找路径,确保能找到外部程序集的页面 options.RootDirectory = "/Pages"; });
3. wwwroot资源隔离
将外部程序集的静态资源映射到/legacy/static路径下,避免与原有wwwroot资源冲突。
第一步:配置外部程序集的嵌入式资源
在外部程序集的csproj文件中添加以下配置,将wwwroot内容打包为嵌入式资源:
<ItemGroup> <EmbeddedResource Include="wwwroot/**/*" /> </ItemGroup>
第二步:在现有项目中映射静态资源
// 注册嵌入式文件提供器 builder.Services.AddSingleton<IFileProvider>( new EmbeddedFileProvider(legacyAssembly, $"{legacyAssembly.GetName().Name}.wwwroot") ); // 映射静态资源到/legacy/static路径 app.UseStaticFiles(new StaticFileOptions { RequestPath = "/legacy/static", FileProvider = new EmbeddedFileProvider(legacyAssembly, $"{legacyAssembly.GetName().Name}.wwwroot") });
之后外部程序集的wwwroot/css/site.css即可通过/legacy/static/css/site.css访问。
4. 关键注意点
- 如果外部程序集的控制器/页面带有自定义路由属性,建议清除原有路由规则(如示例中
controller.Selectors.Clear()),避免同时存在根路由和前缀路由。 - 布局冲突可通过约定强制指定专属布局,或在外部程序集的页面中直接修改
Layout属性。 - 若外部程序集使用了区域(Area),需在路由约定中额外处理区域前缀。
内容的提问来源于stack exchange,提问作者mxcolin
相关产品推荐
相关产品推荐

