ASP.NET Core 2.1 Angular 6.0 SPA模板 - 用于索引而非静态页面的razor页面(cshtml)

时间:2018-06-12 10:54:24

标签: angular asp.net-core angular-cli angular-cli-v6 asp-net-core-spa-services

我正在尝试将ASP.NET Core 2.0 Angular 5设置的webpack应用程序迁移到ASP.NET Core 2.1 Angular 6 Angular-Cli cshtml

简短问题:

如何强制使用Microsoft.AspNetCore.SpaServices.Extensions解析index.html的剃刀页面指令?

我不想使用Angular.json中的index.cshtml,而是使用index.html

详细说明:

我从Angular template开始了一个新的ASP.NET核心项目。有很多东西可以神奇地起作用。

  1. ClientApp/src文件夹中有</body>个文件 应用程序启动时自动提供服务。
  2. 当应用程序运行时,index.html之前的inline.bundle.js标记(从第一点开始)会插入几个脚本:polyfills.bundle.jsvendor.bundle.jsmain.bundle.js,{{在styles.bundle.css标记之前1}}和</head>
  3. 我不确定是谁插入了这些脚本,但我想将它们插入Razor页面(cshtml)。

    我尝试使用旧项目中的代码:

    <!DOCTYPE html>
    <html>
    <head>
        <base href="/" />
        <title>@ViewData["Title"]</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
    @{string googleAnalyticsId = Html.Raw(ViewData["GoogleAnalyticsId"]).ToString();}
    @if (!string.IsNullOrEmpty(googleAnalyticsId))
    {
        <script>
            (function(i, s, o, g, r, a, m)
            {
               ...
            })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
    
            ga('create', '@googleAnalyticsId', 'auto');
        </script>
    }
    <script>
        @Html.Raw(ViewData["TransferData"])
    </script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/0.8.2/css/flag-icon.min.css" async defer />
    <script src="https://apis.google.com/js/platform.js" async defer></script>
    <app>
        <!-- loading layout replaced by app after startupp -->
        <div class="page-loading">
            <div class="bar">
                <i class="sphere"></i>
            </div>
        </div>
    </app>
    </body>
    </html>
    

    并将Angular.json中的索引选项指向此index.cshtml

      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
            "options": {
                "outputPath": "wwwroot/dist",
                "index": "Views/Home/Index.cshtml",   //<----- here
    

    问题是ASP.NET Core不处理内容(所有@标签),但它输出直接内容:

    <!DOCTYPE html>
    <html>
    <head>
        <base href="/" />
        <title>@ViewData["Title"]</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
    @{string googleAnalyticsId = Html.Raw(ViewData["GoogleAnalyticsId"]).ToString();}
    @if (!string.IsNullOrEmpty(googleAnalyticsId))
    {
        <script>
    ...
    

    但它应该是:

    <!DOCTYPE html>
    <html>
    <head>
        <base href="/" />
        <title>Test title</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <script>
    

    我想打印一些变量(从appsettings.json)到结束html页面。由于我已经在使用ASP.NET Core,因此Razor页面似乎是很好的解决方案。

    我也尝试:
    添加新的RazorPage,处理C#内容(@指令),但没有人插入Angular脚本。

    对AndyCunningham的回复回答:

    ng build --prod写下这些文件:

    <script type="text/javascript" src="runtime.b38c91f828237d6db7f0.js"></script><script type="text/javascript" src="polyfills.2fc3e5708eb8f0eafa68.js"></script><script type="text/javascript" src="main.16911708c355ee56ac06.js"></script> 
    

    index.html。如何将此写入剃刀页面。

3 个答案:

答案 0 :(得分:4)

这是一个古老的问题,但我希望答案能帮助某人

好吧,很难让webpack在您的index.cshtml中插入scripts标签,但是您可以像这样

那样将标签包含在index.cshtml中
  $.connection.hub.start().done(function () {
        $('#sendmessage').click(function () {
            // Call the Send method on the hub.
            chat.server.send($('#displayname').val(), $('#message').val());
            // Clear text box and reset focus for next comment.
            $('#message').val('').focus();
        });
    }).fail(function (reason) {
        console.log("SignalR connection failed: " + reason);
    });

然后您可以像路由pvc一样

@{
    ViewData["title"] = "I'am from cshtml";
}
<app-root>Loading...</app-root>
<script type="text/javascript" src="/runtime.js"></script>
<script type="text/javascript" src="/polyfills.js"></script>
<script type="text/javascript" src="/styles.js"></script>
<script type="text/javascript" src="/vendor.js"></script>
<script type="text/javascript" src="/main.js"></script>

请注意,所有与{controller} {action = Index} / {id?}不对应的路由都属于“ spa-fallback”,即如果文件没有.css或.map或.js或。 sockjs为您的家庭/索引提供服务。 NET的“正则表达式”是必需的,而脚本文件则是必需的。

最后一步是使用outputHashing:all

配置您的angular.json以维护脚本的名称。
app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller}/{action=Index}/{id?}");

    routes.MapRoute(
        name: "spa-fallback",
        template: "{*url:regex(^((?!.css|.map|.js|sockjs).)*$)}",
        defaults: new { controller = "Home", action = "Index" });

});

最后,我们可以检查您可以在startup.cs中使用的url是否有效(其他任何url服务器都是索引页)

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ClientApp": {
      ...
      "architect": {
        "build": {
            ...
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all", //<--add this line
                  ...
            },
            ....
  },
  "defaultProject": "ClientApp"
}

注意:我将脚本的href放在绝对路径中,例如src =“ / runtime.js”,在json包中,我们可以有几个脚本,如

app.Use(async (context, next) =>
{
    string path = context.Request.Path.Value.Substring(1);
    bool encontrado = path.EndsWith(".js") || path.EndsWith(".css") || path.EndsWith(".map") || path.StartsWith("sockjs");
    if (!encontrado)
    {
        ..use your code to check the url...
    }
    if (encontrado)
        await next.Invoke(); //<--continue
    else
    {   //send to an error404 page
        context.Response.ContentType = "text/html";
        await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "error404.html"));
    }
});

如果我们使用--serve-path = fr-FR,则脚本的href必须使用此serve-path,例如src =“ / fr-FR / runtime.js”

答案 1 :(得分:2)

我将这个答案基于我对React SPA扩展的经验,但是它对于Angular也应该有所帮助,因为它使用了the same ClientApp/index.html middleware mechanism和相同的IApplicationBuilder.UseSpa入口点。

问题在于,当您使用() VALUES ()'] [parameters: ({}, {}, {}, {}, {}, {}, {}, {} ... displaying 10 of 895203 total bound parameter sets ... {}, {})] (Background on this error at: http://sqlalche.me/e/2j85)时,此扩展名configures a middleware that listens for ALL UI requests and fwds them to index.html的默认实现。

我已经为我们的项目构建了一个简短的示例:https://github.com/andycmaj/aspnet_spa_without_indexhtml/pull/1/files应该向您展示需要更改哪些内容以忽略index.html并使用剃刀视图。

基本上,我已经重新实现了app.UseSpa(...)的功能,但是没有将拦截和转发的中间件附加到index.html。

请注意,UseSpa剃刀视图仍然需要做index.html正在做的更重要的事情。最重要的是,它需要include bundle.js由SPA静态内容中间件提供服务。

答案 2 :(得分:-1)

您处于正确的道路上,您仍然需要_layout.cshtml中拥有的所有内容,但是您没有修改Angular index.html。相反,您的Home/Index.cshtml_layout.cshtml将包含<app-root>元素以及所有的角度脚本。您可能需要禁用生成的角度文件中的哈希值,并使用ASP.NET来使您的JS无效。

此链接详细解释了此问题,但在Angular 6 + ASP.NET Core 2.1中,我仍然遇到一些小问题: Add Angular 5 CLI project to ASP.Net Core 2.0 project