【问题标题】:ASP.NET Core 2.1 Angular 6.0 SPA template - razor page (cshtml) for index instead static pageASP.NET Core 2.1 Angular 6.0 SPA 模板 - 用于索引而不是静态页面的剃须刀页面 (cshtml)
【发布时间】:2018-11-21 18:00:33
【问题描述】:

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

小问题:

如何使用Microsoft.AspNetCore.SpaServices.Extensions 强制解析来自cshtml 的剃须刀页面指令?

我不想使用来自Angular.jsonindex.html,而是使用index.cshtml

详细说明:

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

  1. ClientApp/src 文件夹中有一个index.html 文件,它是 应用程序启动时自动提供服务。
  2. 当应用程序运行时,在index.html</body> 标记之前(从第一点开始)插入几个脚本:inline.bundle.jspolyfills.bundle.jsvendor.bundle.jsmain.bundle.jsstyles.bundle.css</head> 标记之前.

我不确定是谁插入了这些脚本,但我想将它们插入 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。如何将其写入剃须刀页面。

【问题讨论】:

  • 我面临同样的问题,因为新模板没有 Index.cshtml。你的问题解决了吗?如果是,你能分享一下片段吗?
  • 嗨@SagarK 我需要从数据库中写入一些数据,所以我想使用 Razor 页面。目前,我正在 Startup 上编辑静态 Index.html 页面。
  • 好的,但是您如何在启动中配置身份验证,因为没有使用 MapSpaFallbackRoute
  • 这是一个微不足道的问题,我很震惊没有对此的支持。我认为这将是 SEO 的理想功能。总的来说,似乎制作这个系统的人,也许我忽略了其他原因,不知道单一职责原则......你们有没有为此想出任何办法?
  • 您的问题解决了吗?

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


【解决方案1】:

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

问题是当你使用app.UseSpa(...)时,这个扩展的默认实现configuresa 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 并改用 razor 视图。

基本上,我已经重新实现了 UseSpa 所做的事情,但没有将拦截和转发的中间件附加到 index.html。

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

【讨论】:

  • 这看起来很有希望。您能否解释一下这与 Eliseo 解决方案有何不同?它仍然缺少所有脚本,这些脚本是动态创建的。
  • @Makla 脚本都由static/js 提供,因此IsSpaRoute matcher 将所有脚本映射到静态内容服务器。不同之处在于它仍然利用 SpaExtensions 的 IOptionsSpaBuilder,因此您不必自己弄清楚如何提供这些文件。您只需将他们的中间件映射到特定的 Spa 文件路径。
  • @AndyCunnigham 我不明白如何将脚本标签写入剃须刀页面(因为它们是动态的)。你能检查我的问题吗?底部是部分:Response for AndyCunningham answer.
  • @AndyCunningham,我认为他的观点是 Razor 文件试图获取 /static/js/bundle.js 但 webpack 对文件名进行哈希处理以进行缓存破坏,因此必须有人从原始静态 html 文件中读取文件名。
【解决方案2】:

这是一个老问题,但我希望答案对某人有所帮助

嗯,很难让 webpack 在 index.cshtml 中插入脚本标签,但你可以在 index.cshtml 中包含标签

@{
    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>

然后你可以像路由器一样路由你的mvc

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" });

});

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

最后一步是配置您的 angular.json 以使用 outputHashing:all 维护脚本的名称

{
  "$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"
}

最后我们可以检查一个 url 是否有效(否则任何 url 服务器索引页面)你可以在 startup.cs 中使用一些类似

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"));
    }
});

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

"scripts": {
    "ng": "ng",
    "start": "ng serve --base-href=/ --serve-path=/",
    "start:fr": "ng serve --base-href=/fr-FR/ --configuration=fr --serve-path=/fr-FR",
    "build": "ng build",
    ...
  },

如果我们使用--serve-path=fr-FR,脚本的href必须使用这个serve-path,例如src="/fr-FR/runtime.js"

【讨论】:

  • 这对我不起作用。如果我添加索引剃须刀页面,则在应用程序启动时从这里提供内容,但没有人包含 Angular 脚本。
  • @Makla,你是谁必须插入脚本选项卡。在答案的第一个部分查看我的 index.cshtml 。这个想法是您利用 runtime.js、pollyfil.js.. 在服务器中的优势,因此任何 .hmtl 都可以包含。不要忘记您必须维护 UseAngularCliServer。只需要替换 app.UseMvc 并在 index.cshtml 中手动添加脚本
  • 这就是问题所在。版本太多了。这个过程必须是自动化的。不管怎么说,还是要谢谢你。我无法手动为生产环境中的每个脚本添加哈希。
  • 添加哈希?我不确定你是否理解。如果你使用 "outputHashing": "all" 文件总是:runtime.js、polyfills.js、styles.js、vendor.js 和 main.js 没有哈希。无论如何我会尝试做一个github(但我不能保证什么时候)
  • @Eliseo,我很想看看那个 github 仓库。
【解决方案3】:

您走在了正确的道路上,您仍然需要 _layout.cshtml 中的所有内容,但您没有修改 Angular index.html。相反,您的 Home/Index.cshtml_layout.cshtml 将包含 &lt;app-root&gt; 元素以及您的所有角度脚本。您可能希望从生成的 Angular 文件中禁用哈希,并使用 ASP.NET 使您的 JS 无效。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-19
    • 2020-11-01
    • 1970-01-01
    • 2018-09-18
    • 2021-04-19
    • 2019-01-11
    • 1970-01-01
    相关资源
    最近更新 更多