Angular 6 в ASP NET Core 2.1

Tags: .NET, Microsoft

Введение

После серии превью Microsoft анонсировала окончательную версию .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 % своевременным.

Использование кода

Я объясню в этой статье, как я это сделал, как код развился, проблемы, с которыми я столкнулся, и как я тх исправил, но на данный момент, если вам интересно, вы можете пойти в этот репозиторий, клонировать его, установить шаблон и начать разработку Угловое 6 с ASP NET Core 2.1.

Этот шаблон включает:

  1. NET Core API 2.1 (азовый MVC, с ValuesController, как обычно)
  2. Swagger (просто перейдите к своему '/swagger')
  3. Проект Angular 6 с Angular CLI (6.0.7) включая:
    1. MDBootstrap (https://mdbootstrap.com/)
    2. ngx-mapbox-gl (https://github.com/Wykks/ngx-mapbox-gl)
    3. rxjs & rxjs-compat
  4. Поддержка Docker (просто выберите docker-compose as startup project и запустите его)

Произведите установку из git repo

  1. Клонируйте это repo
  2. Установите шаблон dotnet new --install .\content
  3. Создайте каталог, в котором вы хотите сохранить проект
  4. Введите `dotnet new angular6 -n <name_of_your_project>
  5. Откройте решение и запустите проект
  6. Наслаждайтесь кодированием!

Как я это сделал?

Ну, лучшие наборы правил включали те же шаги, чтобы начать с нуля:

  1. Обновите среду (npm, Node и Angular CLI)
  2. Создайте новый проект Angular
  3. Зайдите в новую созданную папку проекта и создайте с помощью dotnet CLI новый проект Angular SPA с «dotnet new angular -n ProjectName»,
  4. Проведите рефакторинг для смешивания обеих экосистем

Этот последний шаг был самым сложным: с одной стороны, у вас есть папка ClientApp, созданная шаблоном SPA, но включающая приложение Angular 4, а также у вас есть папка src с ng -генерированным приложением Angular 6, поэтому, первый шаг для меня состоял в том, чтобы избавиться от каждого материала Angular 4, в том числе удаления webpack.config и многих ретрансляторов .ts, которые сегодня я не могу перечислить. Честно говоря, я попал в цикл неудач, который потреблял мое свободное время в течение последних нескольких недель.

Как это произошло?

И, наконец, я получил рабочую версию, которая удовлетворила меня, без трюков, чистого решения с чистой структурой папок, которая позволила бы мне расти по обе стороны моих разработок, интерфейс с Angular 6 и backend с .NET Core, версия 2 на то время. И на всякий случай я сохранил снимок экрана.

Затем я добавил Swagger с пакетом NuGet от Swasbucle, и я мог видеть свою страницу, тем временем я смог получить доступ к интерфейсу swagger и вызвать контроллер API. И на всякий случай я сохранил снимок экрана.

Затем я принялся за веб-дизайн, и я попробовал Bootstrap, Angular Material и Bootstrap Material Design UI KIT (aka MDBootstrap), я решил использовать MDBootstrap, потому что он мне больше всего нравится. И это сработало, ну, во время компиляции есть несколько предупреждений, но я могу жить с этим. И я добавил фиктивный навигатор и фиктивный нижний колонтитул, а также фиктивную анимацию, и все сработало ... И на всякий случай я сохранил снимок экрана.

И так как в моем проекте мне нужно работать с картами, хотя я уже знал ответ, я попросил лучшую интеграцию с картами в Angular, и мне сказали, чтобы я обратился к сервису карт через ngx-mapbox-gl. И я установил, настроил его более или менее, я вставил карту на сайт, и она сработала ... И на всякий случай я сохранил снимок экрана.

(ПРИМЕЧАНИЕ. Я допустил ошибку, совершаемую всеми новичками, но это не имеет значения, у репо есть мой токен mapbox, он будет работать для всех, но я должен обновить этот пример, чтобы избежать этого, а затем обновить Readme с помощью некоторых команд быстрого запуска.)

И затем, в среду, в 5 утра, я решил поделиться этим с миром, поэтому я создал шаблонный проект, и я кое-что переименовал, чтобы создать трехшаговый шаблон рабочего решения:

  1. Клон репо
  2. Установка шаблона
  3. Создание нового проекта (dotnet new anglular6)

И тогда был выпущен .NET Core 2.1, но поскольку SPA пришел с Angular 5.2 вместо Angular 6, я решил обновить свой проект как Angular 6 и Core 2.1, и он работал, и я подтолкнул его к репо.

И затем меня спросили, могу ли я запустить это в докере. Ну, я пока не пробовал это проделывать с этим решением, и, действительно, поначалу это было одним из самых сложных моментове, потому что я смог запустить все локально, но не внутри контейнера Docker. Я добавил поддержку Docker в проект, и я потратил несколько часов на улучшение этапов докер-файлов, пока не достиг чистого небольшого набора этапов, которые сделали работу довольно быстро, и я подтолкнул ее к репо.

Детали кода

В основном здесь важны два файла. Startup.cs и angular.json, потому что они управляют поведением и взаимодействием между двумя мирами, dotnet и anglular.

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

Как вы можете видеть, запуск включает в себя 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, потому что я потратил, как я сказал ранее, на пару часов, главным образом потому, что я не понимал многоэтапные файлы докеров.

  • 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://nodejs.org/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://nodejs.org/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 в качестве среды сборки.
  • Он устанавливается на один шаг, узел и npm и, наконец, Angular cli.
  • На следующем шаге он попадает в папку и, опять же, в одном RUN, восстанавливает зависимости проекта, пакеты NuGet с восстановлением dotnet и npm с установкой npm.
  • Затем мы переходим к среде выполнения, microsoft/dotnet: 2.1-aspnetcore-runtime, и мы устанавливаем узел npm и угловой cli в этой среде тоже.
  • Здесь мы копируем сборку.
  • И мы запускаем нашу DLL.

Обратите внимание, что для тех, кто использует этот шаблон, в каждом месте при создании проекта с помощью dotnet new, dotnet находит ключевое слово AspNetCore2Ang6Template, оно заменит это на ваше имя проекта. Я использовал этот подход, потому что нашел много POC на GitHub, что в конце подразумевает огромные рефакторинги. Я бы сказал, что с тремя шагами создать проект, который выглядит с нуля, кажется лучшей идеей.

No Comments

Add a Comment