首页 > 解决方案 > c#查看调用Angular组件中断,但直接调用Angular工作正常

问题描述

如何修复我的路由?我有一个带有 Angular 前端的 C# 项目。如果我去调用 Angular 组件的 ac# View,一切都会中断。如果我调用 Angular 视图(直接来自 URL),一切正常。

C# 路由到 ac# 视图

手动路由到 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>
}

调用 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 的屏幕截图

在此处输入图像描述

标签: c#angularroutingangular-routing

解决方案


首先,一个小提示:我遇到了完全相同的问题,但使用的是 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 路由正常工作:

  1. 将映射/TheApp/.../AnotherSPA/作为根路由(因为我们喜欢简短的助记 URL)
  2. 不会弄乱将由 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 路由器NgControllerTheApp

我选择的配置是这样的(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文件夹复制到staticMVC站点内的文件夹中,只是为了澄清命名。“静态”文件夹旨在包含所有(您猜对了)静态内容。我认为 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 使用适当的默认值管理路由(不要启用哈希选项)。

一切都应该正常工作。

希望能帮助到你。


推荐阅读