【问题标题】:.NET core 2.1 base href tag on Angular templateAngular 模板上的 .NET core 2.1 基础 href 标记
【发布时间】:2018-06-07 16:21:52
【问题描述】:

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

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

<base href="~/" />

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

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

谢谢

【问题讨论】:

    标签: angular single-page-application asp.net-core-2.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>
    

    我们可以改变我们的 package.json 的 base href。但如果不相应地更改 --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///home。我们希望基础继续/。看看脚本并按照你的需要,例如

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

    【讨论】:

    • npm i 之后更改 node_modules 中的 webpack 文件听起来不是一个好主意。非常肮脏和脆弱的黑客,它将在下一个npm i 被覆盖,并且它在源代码控制中不可持久
    猜你喜欢
    • 1970-01-01
    • 2018-07-01
    • 2019-01-11
    • 2019-01-03
    • 2019-02-11
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多