c# - c#查看调用Angular组件中断,但直接调用Angular工作正常
问题描述
如何修复我的路由?我有一个带有 Angular 前端的 C# 项目。如果我去调用 Angular 组件的 ac# View,一切都会中断。如果我调用 Angular 视图(直接来自 URL),一切正常。
C# 路由到 ac# 视图
- 如果我在 startup.cs 中正确路由,我会转到:
xxx/Home/index
这只是一个调用 Angular 组件的视图(它会引发一堆 500 错误)
手动路由到 Angular
- 如果我手动添加
/anything
到 url (xxx/Home/Index/anything
),Angular 路由会接管并且一切正常。
索引方法调用
public class HomeController : Controller
{
public IActionResult Index()
{
return View("IndexAng");
}
}
索引Ang.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>
}
从 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 的屏幕截图
解决方案
首先,一个小提示:我遇到了完全相同的问题,但使用的是 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) - 不会弄乱将由 Angular 处理的路由部分。
一些路线以及它们的行为方式:
https://server:port
应该重定向到的 URLhttps://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
应该由 ANDTheApp
中的操作提供服务,之后的所有内容都应该保持原样并直接进入 Angular 路由器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 路由和控制器对于重定向部分和“do-not-mess-with-everything-after-the-app-name”部分来说很好并且可测试。;)
我们现在必须设置托管 Angular 应用程序所需的唯一一点 MVC 视图。我们需要一个用于“Ng”控制器的“Ui”视图。视图中唯一真正重要的<base>
部分是 html 文档头部的标记。我个人将dist
文件夹复制到static
MVC站点内的文件夹中,只是为了澄清命名。“静态”文件夹旨在包含所有(您猜对了)静态内容。我认为 MVCContent
默认创建一个文件夹,但我从不喜欢这个名字。
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>
AAAAAnd... 我们已经完成了 MVC 方面的工作。
在客户端,我们只需要确保 Angular 使用适当的默认值管理路由(不要启用哈希选项)。
一切都应该正常工作。
希望能帮助到你。
推荐阅读
- r - 使用粘贴连接文本以调用 r 中的向量
- javascript - 使用 JavaScript 的 HTML 中未显示数据
- c++ - 从文件输入和输出
- node.js - 如何通过 CodeDeploy 在 AWS EC2 上构建前端?
- python - 如何在我的损失函数中添加 L2 正则化项
- javascript - Bootstrap javascript在Django中不起作用
- .htaccess - 使用具有根目录的 htaccess 文件在 php 中重写 URL
- excel - Excel 从非堆叠单元格创建数组
- c# - 声明带参数和不带参数接收的通用委托并将其添加到字典 C#
- excel - 使用 StyleFrame 的 to_excel 方法将多个 python 数据框复制到 excel 中