4

我在 .NET Core 2.1 + Angular 5 模板的基础上为我们的团队构建了一个模板,该模板包含在最新版本的核心 2.1 中,我们将应用程序部署到虚拟文件夹中,例如 /it/myapp 或 /aa/myotherapp

在 2.0 模板上,基本 href 属性将自动设置,我假设因为它是用 razor 构建的,如下所示:

<base href="~/" />

但是,对于 2.1 模板来说这不是真的,我假设这是因为模板实际上只使用静态文件,新的 app.UseSpa()

关于如何自动填充基本 href 标签的任何想法?

谢谢

4

1 回答 1

1

是的,我知道这是一个老问题,但我希望这能帮助更多人。答案是关于 Angular 6 和 .NET Core 2.1。忠告:解释有点大(对不起)

在 Core 2.1 中,我们有一个典型的 Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
     }
     app.UseHttpsRedirection();
     app.UseStaticFiles();
     app.UseMvc(routes =>
     {
          routes.MapRoute(
          name: "default",
          template: "{controller}/{action=Index}/{id?}");
      });
      app.UseSpa(spa =>
      {
           // To learn more about options for serving an Angular SPA from ASP.NET Core,
           // see https://go.microsoft.com/fwlink/?linkid=864501

           spa.Options.SourcePath = "ClientApp";
           spa.Options.DefaultPage = $"/index.html";

           if (env.IsDevelopment())
           {
               spa.UseAngularCliServer(npmScript: "start");
           }
      });
  }

重要的部分是

   spa.UseAngularCliServer(npmScript: "start");

这告诉 .NET 使用 package.json 中定义的脚本

{
  "name": "client-app",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --base-href=/ --serve-path=/", //<--this line
    "build": "ng build",
     ...
  },
  ....
}

看到我们有两个选择。--base-href 和 --serve-path

当我们执行应用程序时,我们收到了一个 index.html,如

<!DOCTYPE html>
<html>
<head>
  <title>Angular i18n example</title>
  <base href="/"
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <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>
</body>
</html>

我们可以改变基本的 href 改变我们的 package.json。但是,如果不相应地更改 --serve-path,我们的应用程序将失败,因为我们正在根据基本 href 查找脚本。

这个想法是脚本不依赖于基本href。我希望脚本的 src 位于绝对路径中。这就是脚本变得像

    <script type="text/javascript" src="/<serve-path>/runtime.js"></script>
    <script type="text/javascript" src="/<serve-path>/polyfills.js"></script>
    <script type="text/javascript" src="/<serve-path>/styles.js"></script>
    <script type="text/javascript" src="/<serve-path>/vendor.js"></script>
    <script type="text/javascript" src="/<serve-path>/main.js"></script>

所以,如果我们在 .html 中写一些类似

<head>
  <title>Angular i18n example</title>
  <script>
     var str = document.location.href;
     ...
     document.write('<base href="' + str+ '" />');
  </script>
  <!--we remove base href="/"
      <base href="/"
  -->
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

我们的脚本正在从正确的路径中寻找并且应用程序正常工作。¿如何改变添加脚本的方式?好吧,我们必须对两个文件进行一些更改:

  • index-html-webpack-plugin.js 来自 node_modules\@angular-devkit\build-angular\src\angular-cli-files\plugins

  • browser.js 来自 node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\webpack-configs

首先查看 index-html-webpack-plugin.js 中的类 IndexHtmlWebpackPlugin

class IndexHtmlWebpackPlugin {
    constructor(options) {
        this._options = Object.assign({ input: 'index.html', output: 'index.html', entrypoints: ['polyfills', 'main'], sri: false }, options);
    }
    apply(compiler) {
      ....
        const scriptElements = treeAdapter.createDocumentFragment();
        for (const script of scripts) {
            const attrs = [
                { name: 'type', value: 'text/javascript' },
                //change the line
                //{ name: 'src', value: (this._options.deployUrl || '') + script },
                //by 
                { name: 'src', value: (this._options.baseHref)+(this._options.deployUrl || '') + script },
            ];
            if (this._options.sri) {
                const content = compilation.assets[script].source();
                attrs.push(...this._generateSriAttributes(content));
            }
            const element = treeAdapter.createElement('script', undefined, attrs);
            treeAdapter.appendChild(scriptElements, element);
        }

看看我们如何将 this._options.baseHref 添加到脚本中好吧,实际上在构造函数中我们没有 baseHref,这是因为我们必须更改文件 browser.js

plugins: extraPlugins.concat([
    new index_html_webpack_plugin_1.IndexHtmlWebpackPlugin({
        input: path.resolve(root, buildOptions.index),
        output: path.basename(buildOptions.index),
        baseHref: buildOptions.baseHref,
        entrypoints: package_chunk_sort_1.generateEntryPoints(buildOptions),
        deployUrl: buildOptions.deployUrl,
        //we add this line
        servePath: buildOptions.servePath,
        sri: buildOptions.subresourceIntegrity,
    }),
]),

更进一步。由于我们在 index.html 中使用 javascript 编写了基本标记,因此我们不希望 angular-cli 添加此标记。为此,我们唯一必须更改的是注释 index-html-webpack-plugin.js 中的行

// Adjust base href if specified
if (typeof this._options.baseHref == 'string') {
    let baseElement;
    for (const headChild of headElement.childNodes) {
        if (headChild.tagName === 'base') {
            baseElement = headChild;
        }
    }
    const baseFragment = treeAdapter.createDocumentFragment();
    if (!baseElement) {
        baseElement = treeAdapter.createElement('base', undefined, [
            { name: 'href', value: this._options.baseHref },
        ]);
//coment the two lines below
//        treeAdapter.appendChild(baseFragment, baseElement);
//        indexSource.insert(headElement.__location.startTag.endOffset + 1, parse5.serialize(baseFragment, { treeAdapter }));
    }

就这样 !!

还有两个兴趣点

当我们使用 javascript 创建基本标签时,我们需要调整基本标签。否则,如果我们刷新页面,我们的基本href 会改变,如果我们在页面主页中,我们的url 将类似于http://www.dominio.com/<something>/<something>/home。我们希望基础继续<something>/<something>。看看脚本并按照您的意愿进行操作,例如

  <script>
    var items = document.location.href.split('/');
    while (items.length>4)
      items.pop();
    document.write('<base href="' + items.join('/') + '" />');
  </script>

最后一点,如果使唯一有效的 url 是服务器作为 SPA。为此,我们修改了我们的 startup.cs,使唯一有效的 url 是服务器。(否则,一些像http://www.dominio.com/patata/patata这样的 url为我们的 SPA 服务)在 app.UseMvc 和 app.UseSpa 之间添加一个中间件

   app.UseMvc(routes =>
   {
    ...
    });
   //Add this Middleware
    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)
        {
            //make here the check for the pages
            //"silly" e.g.
            encontrado==(path=="es/adminitrator" || path=="it/administrator")
        }
        if (encontrado)
            await next.Invoke();
        else
        {
            context.Response.ContentType = "text/html";
            await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "error404.html"));
        }
        app.UseSpa(spa=>
        {
              ....
        }
    });

更新

使用 java 脚本添加标签库,这是一个非常丑陋的解决方法。更好地使用 BASE_REF Provider

import {Component, NgModule} from '@angular/core';
import {APP_BASE_HREF} from '@angular/common';

@NgModule({
  providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
})
class AppModule {}
于 2018-07-28T12:05:47.150 回答