c#视图调用Angular组件中断,但是直接调用Angular可以正常工作

时间:2018-09-21 18:58:29

标签: c# angular routing angular-routing

如何修复路由?我有一个带有Angular前端的C#项目。如果我转到调用Angular组件的c#视图,则一切都会中断。如果我直接从URL调用Angular视图,则一切正常。

C#路由到c#视图

  • 如果我在startup.cs中正确路由,请转到: 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#路由时出现

错误: enter image description here

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

enter image description here

1 个答案:

答案 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路由正常工作:

  1. /TheApp/.../AnotherSPA/映射为根路由(因为我们喜欢短助记符URL)
  2. 不会弄乱Angular将要处理的部分路由。

一些路线及其行为方式:

  • 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可以管理路由 使用适当的默认值(请勿启用哈希选项)。

一切都应该正常工作。

希望有帮助。