Angular 6 в ASP NET Core 2.1
Введение
После серии превью 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.
Этот шаблон включает:
- NET Core API 2.1 (азовый MVC, с ValuesController, как обычно)
- Swagger (просто перейдите к своему '/swagger')
- Проект Angular 6 с Angular CLI (6.0.7) включая:
- MDBootstrap (https://mdbootstrap.com/)
- ngx-mapbox-gl (https://github.com/Wykks/ngx-mapbox-gl)
- rxjs & rxjs-compat
- Поддержка Docker (просто выберите docker-compose as startup project и запустите его)
Произведите установку из git repo
- Клонируйте это repo
- Установите шаблон dotnet new --install .\content
- Создайте каталог, в котором вы хотите сохранить проект
- Введите `dotnet new angular6 -n <name_of_your_project>
- Откройте решение и запустите проект
- Наслаждайтесь кодированием!
Как я это сделал?
Ну, лучшие наборы правил включали те же шаги, чтобы начать с нуля:
- Обновите среду (npm, Node и Angular CLI)
- Создайте новый проект Angular
- Зайдите в новую созданную папку проекта и создайте с помощью dotnet CLI новый проект Angular SPA с «dotnet new angular -n ProjectName»,
- Проведите рефакторинг для смешивания обеих экосистем
Этот последний шаг был самым сложным: с одной стороны, у вас есть папка 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 утра, я решил поделиться этим с миром, поэтому я создал шаблонный проект, и я кое-что переименовал, чтобы создать трехшаговый шаблон рабочего решения:
- Клон репо
- Установка шаблона
- Создание нового проекта (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, что в конце подразумевает огромные рефакторинги. Я бы сказал, что с тремя шагами создать проект, который выглядит с нуля, кажется лучшей идеей.