如何修复路由?我有一个带有Angular前端的C#项目。如果我转到调用Angular组件的c#视图,则一切都会中断。如果我直接从URL调用Angular视图,则一切正常。
C#路由到c#视图
xxx/Home/index
只是调用Angular组件的View(会引发500个错误)手动路由到Angular
/anything
手动添加到url(xxx/Home/Index/anything
)中,则Angular路由将接管一切,一切正常。索引方法调用
public class HomeController : Controller
{
public IActionResult Index()
{
return View("IndexAng");
}
}
IndexAng.cshtml
@{
ViewData["Title"] = "Home Page";
}
@*<script src="https://npmcdn.com/tether@1.2.4/dist/js/tether.min.js"></script>*@
@*<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>*@
<h3>Loading Ang App root:</h3>
<app-root></app-root>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}
调用c#路由时出现
从Startup.cs配置方法
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext identityContext,
UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
{
#if DEBUG
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
#else
app.UseExceptionHandler("/Home/Error");
#endif
app.UseStaticFiles();
//app.UseSession();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
}
尝试导航到main-client.js的屏幕截图
答案 0 :(得分:3)
首先,请注意:我遇到了完全相同的问题,但是使用的是ASP.NET MVC 5。 因此,我不能保证100%会使用相同的精确解决方案。
但是,我相当确定我要描述的技术是可靠的,并且 至少可以毫不费力地进行调整。
问题的根源:为了拥有ASP.NET MVC(简称MVC) 和Angular 2+并存,他们必须知道路由处理的位置 服务器的责任将终止,客户端的责任将在何处开始。
我们还必须了解MVC如何在提供路由时简化地址 请求。
我主张的解决方案是创建一个称为NgController
的控制器,
它将为以下地区的所有单页应用程序(即现在的SPA)提供服务
您的安装。
NgController
的草图是这样:
public class NgController : Controller
{
private boolean Authenticated()
{
// returns true if the user is authenticated. This system can
// (and probably should) be integrated or replaced with
// one of the ASP.NET Core authentication modules.
return true;
}
public ActionResult Index()
{
if (!Authenticated())
return RedirectToAction("Login", "Authentication", new { ReturnUrl = HttpContext?.Request?.Path });
// Index does not serve directly the app: redirect to default one
return RedirectToAction("TheApp");
}
// One action for each SPA in your installment
public ActionResult TheApp() => UiSinglePageApp("TheApp");
public ActionResult AnotherSPA() => UiSinglePageApp("AnotherSPA");
// The implementation of every SPA action is reusable
private ActionResult UiSinglePageApp(string appName)
{
if (!Authenticated())
return RedirectToAction("Login", "Authentication", new { ReturnUrl = HttpContext?.Request?.Path });
return View("Ui", new SinglePageAppModel { AppName = appName });
}
}
真正的基本知识:我们只需要确保默认的Index
操作不会直接为任何应用提供服务。
然后,我们需要确保MVC路由正常工作:
/TheApp/...
和/AnotherSPA/
映射为根路由(因为我们喜欢短助记符URL)一些路线及其行为方式:
https://server:port
应该重定向到https://server:port/TheApp
https://server:port/TheApp
应该由TheApp
中的NgController
操作提供服务https://server:port/AnotherSPA
应该由AnotherSPA
中的NgController
操作提供服务https://server:port/TheApp/some/token/and/subroute/for/angular/deep/linking
应该由
TheApp
中的NgController
操作以及TheApp
之后的所有内容应保持原样并直接进行
到角路由器我的配置选择是这样(MVC 5,需要对MVC Core 2进行一些调整):
public static void RegisterRoutes(RouteCollection routes)
{
// the Ng controller is published directly onto the root,
// the * before id makes sure that everything after the action name
// is kept as-is
routes.MapRoute("Ng",
"{action}/{*id}",
new {controller = "Ng", id = UrlParameter.Optional},
new {action = GetUiConstraint()}
);
// this route allows the mapping of the default site route
routes.MapRoute(
"Default",
"{controller}/{action}/{*id}",
new { controller = "Ng", action = "Index", id = UrlParameter.Optional }
);
}
/// <summary> The Ui constraint is to be used as a constraint for the Ng route.
/// It is an expression of the form "^(app1)|(app2)$", with one branch for
/// each possible SPA. </summary>
/// <returns></returns>
public static string GetUiConstraint()
{
// If you don't need this level of indirection, just
// swap this function with the result.
// return "^(TheApp)|(AnotherSPA)$";
var uiActions = new ReflectedControllerDescriptor(typeof(NgController))
.GetCanonicalActions()
.Select(x => x.ActionName.ToLowerInvariant())
.Where(x => x != "index")
.Select(x => "(" + x + ")");
return string.Concat("^", string.Join("|", uiActions), "$");
}
现在,对于重定向部分,MVC路由和Controller很好且可测试 以及“在应用名称后不包含所有内容”部分。 ;)
我们现在必须设置托管Angular应用所需的MVC视图的唯一位。
对于“ Ng”控制器,我们需要一个“ Ui”视图。唯一真正重要的一点
视图的顶部是html文档头部的<base>
标签。
我个人将dist
文件夹复制到MVC网站内的static
文件夹中,
只是为了澄清命名。 “静态”文件夹用于包含所有
(您猜对了)静态内容。我认为MVC创建了一个Content
文件夹,
默认,但是我从不喜欢这个名字。
MVCroot
|-static
|-ng
|-TheApp (this is the dist folder for TheApp)
|-AnotherSPA (this is the dist folder for AnotherSPA)
我还将每个js文件打包到一个名为SPA的文件中,但这不是必需的。
而且我不喜欢局部元素,只是因为我不需要布局,所以我将所有东西都放在一起。
YMMV。
Ui.cshtml
@{
Layout = null;
var baseUrl = Url.Content("/") + Model.AppName;
var appName = Model.AppName.ToLowerInvariant();
}
<!DOCTYPE html>
<html style="min-height: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="~/static/favicon.ico">
<title>My marvelous application</title>
@Url.CssImportContent("static/ng/{appName}/css/main.css")
<!-- This is the CRUCIAL bit. -->
<base href="@baseUrl">
</head>
<body>
<app-root>Loading...</app-root>
@Url.ScriptImportContent($"~static/ng/{appName}/main.js")
</body>
</html>
AAAA ...我们在MVC方面已经完成。
在客户端,我们只需要确保Angular可以管理路由 使用适当的默认值(请勿启用哈希选项)。
一切都应该正常工作。
希望有帮助。