Angular 2与ASP.NET Core 2混合路由项目中跳转至Razor页面的疑问
兄弟,我之前刚好折腾过Angular 2和ASP.NET Core 2混合路由的场景,你的问题我太懂了——既要跳出Angular路由访问Razor页面,又怕遇到未知路由时陷入无限循环对吧?给你几个实测有效的方案:
方案1:让ASP.NET Core优先处理指定路由前缀
最稳妥的方式是在后端把Razor页面的路由优先级提上来,让ASP.NET Core先匹配这些路由,剩下的再交给Angular处理。这样/documents/view/这类Razor请求根本不会进入Angular的路由系统,自然不会有循环问题。
修改Startup.cs里的Configure方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 先加其他必要的中间件(比如异常处理、静态文件) if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 先注册ASP.NET Core的MVC/Razor路由 app.UseEndpoints(endpoints => { // 专门给你的documents路由单独配置,确保优先匹配 endpoints.MapControllerRoute( name: "documents", pattern: "documents/{action=View}/{id?}", defaults: new { controller = "Documents" }); // 通用MVC路由 endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); // 如果用的是Razor Pages,加上这个 endpoints.MapRazorPages(); }); // 最后处理Angular SPA:只处理非/documents开头的请求 app.MapWhen(context => !context.Request.Path.StartsWithSegments("/documents"), builder => { builder.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { spa.UseAngularCliServer(npmScript: "start"); } }); }); }
这里用MapWhen把Angular的SPA中间件限定在非/documents的路径下,确保所有/documents开头的请求直接走ASP.NET Core。
方案2:在Angular路由里主动跳转到外部URL
如果不想动后端配置,也可以在Angular的路由模块里,把需要跳去Razor页面的路径直接通过window.location.href跳转,而不是用Angular的路由导航。
先在app-routing.module.ts里配置路由规则:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { PendingTransactionsComponent } from './pending-transactions/pending-transactions.component'; import { RouteResolver } from './route.resolver'; const routes: Routes = [ { path: 'PendingTransactions', component: PendingTransactionsComponent }, // 匹配/documents开头的路径,直接触发解析器跳转 { path: 'documents**', resolve: { url: RouteResolver } }, // 其他Angular路由... // 最后处理未知路由,判断是否属于ASP.NET的路由范围 { path: '**', resolve: { url: RouteResolver }, component: NotFoundComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [RouteResolver] }) export class AppRoutingModule { }
然后写一个RouteResolver服务来处理跳转逻辑:
import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, EMPTY } from 'rxjs'; @Injectable() export class RouteResolver implements Resolve<any> { resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> { const targetUrl = state.url; // 定义哪些路径需要交给ASP.NET Core处理 const aspNetRoutes = ['/documents', '/account', '/admin']; if (aspNetRoutes.some(routePath => targetUrl.startsWith(routePath))) { // 用window.location.href发起新请求,跳出Angular路由 window.location.href = targetUrl; return EMPTY; } // 其他未知路由返回NotFound组件 return EMPTY; } }
这样当用户访问/documents/view/时,Angular会触发这个解析器,直接让浏览器发起新请求,交给ASP.NET Core处理,不会留在Angular的路由循环里。
方案3:彻底解决未知路由的循环陷阱
你之前遇到的循环问题,本质是当ASP.NET和Angular都不识别某个路由时,Angular捕获后又重新加载,ASP.NET又把请求丢回给Angular,无限循环。解决这个的核心是:只把Angular能处理的路由交给它,真正的404让ASP.NET返回。
修改Startup.cs的Configure方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件... app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 先处理MVC/Razor路由 app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapRazorPages(); }); // 处理Angular SPA,但只在请求的文件存在时返回index.html app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; spa.Options.DefaultPage = "/index.html"; // 关键:如果请求的不是index.html且文件不存在,直接返回404 spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions { OnPrepareResponse = ctx => { if (!ctx.Context.Request.Path.Value.Equals("/index.html", StringComparison.OrdinalIgnoreCase)) { ctx.Context.Response.StatusCode = StatusCodes.Status404NotFound; } } }; if (env.IsDevelopment()) { spa.UseAngularCliServer(npmScript: "start"); } }); }
这样当访问一个既不是ASP.NET路由也不是Angular路由的路径时,ASP.NET会直接返回404,而不是把请求丢给Angular,彻底避免循环。
内容的提问来源于stack exchange,提问作者Ben Weidberg

