65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular 6 within ASP NET Core 2.1

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (15投票s)

2018 年 6 月 2 日

CPOL

7分钟阅读

viewsIcon

63406

融合两个世界的最简单方法。

引言

在经过一系列预览版之后,微软发布了 .NET Core 2.1 的最终版本,其中包含了 ASP.NET Core 和 Entity Framework Core 的新版本。正如你可能知道的,官方的 Angular SPA 模板使用的是 Angular 4,而在 .NET Core 2.1 发布后的几小时内,我尝试了新的 Angular 模板,它使用的是 5 版本,这让我很失望。

背景

在过去的几个月里,世界各地的许多开发者一直在尝试将 ASP.NET Core 和 Angular 6 结合起来,是的,我阅读并尝试了几乎所有博客文章中的方法,但都没有取得好结果,有些方法对我来说有效率达到 85%,有些则完全无效,所以我决定从头开始创建我自己的方法。我结合了所有这些文章中看到的步骤和技术,最终奏效了。

那时,我的一个朋友向我要这个方法,但由于时间不足,我决定给他一个模板,让他用几个命令就能创建一个可用的项目。那发生在周二晚上,我创建了一个 GitHub 仓库,并在周三早上上班前与大家分享,但就在那时,.NET Core 2.1 发布了,我感到有必要更新仓库,使其达到 100% 最新的状态。

Using the Code

在这篇文章中,我将解释我是如何做到的,它是如何演变的,我面临的挑战以及我如何解决它们。但此刻,如果你好奇,你可以去那个仓库,克隆它,安装模板,然后开始使用 ASP.NET Core 2.1 开发 Angular 6。

此模板包含

  1. NET Core API 2.1(基础 MVC,像往常一样带有 ValuesController
  2. Swagger(只需导航到你的 '/swagger')
  3. Angular 6 项目,包含 Angular CLI(6.0.7),包括
    1. MDBootstraphttps://mdbootstrap.com/
    2. ngx-mapbox-glhttps://github.com/Wykks/ngx-mapbox-gl
    3. rxjs & rxjs-compat
  4. Docker 支持(只需选择 docker-compose 作为启动项目并运行它)

从 git 仓库安装

  1. Git 克隆此仓库
  2. 安装模板 dotnet new --install .\content
  3. 创建你想存放项目的目录
  4. 键入 `dotnet new angular6 -n <你的项目名称>
  5. 打开解决方案并运行项目
  6. 尽情编码吧!

我是如何做到的?

嗯,最好的方法都同意从零开始的相同步骤,那就是:

  1. 更新你的环境(npm、Node 和 Angular CLI)
  2. 创建一个新的 Angular 项目
  3. 进入新生成的项目文件夹,使用 dotnet CLI 创建一个新的 Angular SPA 项目,命令为 'dotnet new angular -n ProjectName'
  4. 重构以混合两个生态系统

最后一步是最具挑战性的。一方面,你有 SPA 模板生成的 ClientApp 文件夹,其中包含一个 Angular 4 应用程序,同时你还有包含 ng-generated Angular 6 应用程序的 src 文件夹。因此,我的第一步是摆脱所有 Angular 4 的东西,这包括移除 webpack.config 和许多 .ts 的重构,今天我无法列出所有这些。说实话,我进入了一个尝试-失败的循环,消耗了我过去几周的业余时间。

它是如何演变的?

最后,我得到了一个令我满意的可用版本,没有技巧,一个干净的解决方案,拥有干净的文件夹结构,可以让我同时在前端 Angular 6 和后端 .NET Core(当时是 2.1 版本)两个方面发展。我保存了一个快照以备不时之需。

然后我添加了 Swagger,使用 Swasbuckle 的 NuGet 包,我可以在访问 swagger 界面和调用 API Controller 的同时看到我的页面。我保存了一个快照以备不时之需。

然后我开始考虑网页样式,我尝试了 Bootstrap、Angular Material 和 Bootstrap Material Design UI KIT(又名 MDBootstrap)。我决定选择 MDBootstrap,因为我最喜欢它。它奏效了,嗯,编译时有一些警告,但我可以接受。我添加了一个虚拟导航栏、一个虚拟页脚和一个虚拟动画,一切都奏效了……我保存了一个快照以备不时之需。

由于我的项目需要处理地图,即使我知道答案,我也询问了 Angular 中最佳的地图集成方法,有人建议我使用 ngx-mapbox-gl 来集成 mapbox。我尝试了,安装并配置了它,插入了一个地图到网站上,奏效了……我保存了一个快照以备不时之需。

(注意:我犯了一个新手错误,但没关系,仓库中有我的 mapbox token,它对每个人都有效,但我必须更新示例以避免这种情况,然后更新 Readme 添加一些快速入门说明。)

然后,在周三早上 5 点,我决定与世界分享,所以我创建了一个模板项目,并重命名了一些东西,以便模板可以通过三个步骤创建一个可用的解决方案:

  1. 克隆仓库
  2. 安装模板
  3. 创建新项目(dotnet new angular6)

然后 .NET Core 2.1 发布了,但由于 SPA 自带的是 Angular 5.2 而不是 Angular 6,我决定将我的项目更新为 Angular 6 和 Core 2.1,它奏效了,我把它推到了仓库。

然后,有人问我是否能在 docker 中运行它,嗯,我还没有真正尝试过这个解决方案,而且,这确实是最初的挑战之一,因为我能够在本地运行所有东西,但在 Docker 容器中不行。我为项目添加了 Docker 支持,并花费了几个小时改进 Docker 文件阶段,直到我达到了一组简洁的小阶段,能够快速完成工作,然后我把它推到了仓库。

代码详情

这里主要有两个文件很重要:Startup.csangular.json,因为这些文件协调了两个世界(dotnet 和 angular)之间的行为和交互。

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.IO;

namespace AspNetCore2Ang6Template
{
  /// <summary>
  /// Startup class
  /// </summary>
  public class Startup
  {
    /// <summary>
    /// Configures app the services.
    /// </summary>
    /// <param name="services">The services.</param>
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddMvc();
      services.AddSpaStaticFiles(c =>
      {
        c.RootPath = "wwwroot";
      });

      services.AddSwaggerGen(c =>
      {
        c.SwaggerDoc("v1", new Info
        {
          Version = "v1",
          Title = "AspNetCore2Ang6Template API",
          Description = "A simple example ASP.NET Core Web API",
          Contact = new Contact { Name = "Juan García Carmona", 
                    Email = "d.jgc.it@gmail.com", Url = "https://wisegeckos.com" },
        });
        // Set the comments path for the Swagger JSON and UI.
        var basePath = AppContext.BaseDirectory;
        var xmlPath = Path.Combine(basePath, "AspNetCore2Ang6Template.xml");
        c.IncludeXmlComments(xmlPath);
      });
    }

    /// <summary>
    /// Configures the application.
    /// </summary>
    /// <param name="app">The application.</param>
    /// <param name="env">The hosting environment</param>
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
      }

      app.UseDefaultFiles();
      app.UseStaticFiles();

      // Enable middleware to serve generated Swagger as a JSON endpoint.
      app.UseSwagger();

      // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
      // specifying the Swagger JSON endpoint.
      app.UseSwaggerUI(c =>
      {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
      });
      app.UseStaticFiles();
      app.UseSpaStaticFiles();
      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 = "wwwroot";

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

正如你所见,startup 文件混合了 MVC 和 SPA,使用了众所周知的 'wwwroot' 文件夹作为源路径。

Angular.json(仅重要部分)

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "AspNetCore2Ang6Template": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {
        ":component": {
          "styleext": "scss"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "wwwroot",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "node_modules/font-awesome/scss/font-awesome.scss",
              "node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
              "node_modules/angular-bootstrap-md/scss/mdb-free.scss",
              "node_modules/mapbox-gl/dist/mapbox-gl.css",
              "src/styles.scss"
            ],
            "scripts": [
              "node_modules/chart.js/dist/Chart.js",
              "node_modules/hammerjs/hammer.min.js"
            ]
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "AspNetCore2Ang6Template:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "AspNetCore2Ang6Template:build:production"
            }
          }
        }, [ETC...]
  • 它将源根设置为 src,这意味着应用程序的源代码位于 src 文件夹下。
  • 它还添加了 scss 支持,因为这是 MDBootstrap 的要求。
  • 它确定输出路径是 wwwroot,这是最合理的,而且,我至少没有在其他地方看到过这种直接且干净的方法。
  • 它在生产模式下构建时替换了环境变量配置。

此外,我为这个 Docker 文件感到自豪,因为正如我之前所说的,我花了好几个小时,主要是因为我还没有理解多阶段 Docker 文件。

FROM microsoft/dotnet:2.1-sdk as build-env
WORKDIR /app

# Setup node
ENV NODE_VERSION 8.11.1
ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60

RUN curl -SL "https://node.org.cn/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" 
         --output nodejs.tar.gz \
    && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
    && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
    && rm nodejs.tar.gz \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs\
    && npm i -g @angular/cli npm i -g @angular/cli

# Copy csproj and restore as distinct layers
COPY src/AspNetCore2Ang6Template/ ./AspNetCore2Ang6Template/
RUN cd AspNetCore2Ang6Template \
    && dotnet restore \
    && npm install

RUN cd AspNetCore2Ang6Template \
    && dotnet publish -c Release -o out

# build runtime image
FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app

# Setup node
ENV NODE_VERSION 8.11.1
ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60

RUN curl -SL "https://node.org.cn/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" 
         --output nodejs.tar.gz \
    && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \
    && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \
    && rm nodejs.tar.gz \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs\
    && npm i -g @angular/cli

COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "AspNetCore2Ang6Template.dll"]

Article Improvements
  • 它使用最新的 microsoft/dotnet 镜像作为构建环境。
  • 它在一个步骤中安装了 node、npm,最后是 Angular cli。
  • 在下一步中,它进入文件夹,再次在一个 RUN 命令中,它通过 dotnet restore 恢复项目依赖项,NuGet 包,以及通过 npm install 恢复 npm 包。
  • 然后我们切换到运行时环境,microsoft/dotnet:2.1-aspnetcore-runtime,并在此环境中也安装 node、npm 和 angular cli。
  • 我们将构建环境的输出复制到这里。
  • 然后运行我们的 DLL。

请注意,对于使用此模板的任何人,在项目创建过程中(使用 dotnet new)时,dotnet 会找到 AspNetCore2Ang6Template 关键字,并将其替换为您自己的项目名称。我之所以采用这种方法,是因为我在 GitHub 上发现了许多 POC 项目,最终需要大量的重构。我认为,通过三个步骤创建一个看起来像是从头开始的项目,是一个更好的主意。

结束语

我相信这能帮助许多其他开发者,同时我也愿意提供直接帮助,所以请不要犹豫留下评论,或者通过电子邮件或 LinkedIn (很容易找到我) 直接联系我。一如既往,希望它有所帮助。

历史

  • 文章创建于 2018 年 6 月 2 日,作者:Juan García Carmona
  • 几分钟后:添加了 Startup.csAngular.json 代码
  • 又过了几分钟,我添加了 Docker 文件及其说明
© . All rights reserved.