Введение в ASP.NET Core и Angular

Первый проект на ASP.NET Core с Angular

Последнее обновление: 26.06.2020

В этом руководстве рассмотрим создание приложения на ASP.NET Core 3 в связке с Angular 10.

Вначале создадим проект на ASP.NET Core, который будет использовать Angular. Для проекта выберем шаблон Empty, то есть пустой шаблон.

Angular in ASP.NET Core

Пусть проект будет называться "HelloAngularApp". Стоит отметить, что Visual Studio предоставляет шаблон Angular, который сразу же позволяет создать проект с некоторой базовой структурой и с уже настроенной конфигурацией на использование Angular. Но в данном случае выберем именно шаблон Empty, чтобы полностью проконтроллировать процесс конфигурации и не создавать ничего лишнего.

По умолчанию новый проект ASP.NET Core не обладает никакой встроенной поддержкой работы с Angular. Один из способов работы с Angular в рамках проекта ASP.NET Core представляет пакет Microsoft.AspNetCore.SpaServices.Extensions или проще говоря SpaServices. Поэтому вначале установим данный пакет через Nuget:

SpaServices и Angular в ASP.NET Core

После добавления пакета создадим в проекте новую папку, которая будет называться ClientApp и которая впоследствии будет хранить файлы и конфигурацию приложения Angular.

Далее добавим в только что созданную папку ClientApp новый файл package.json, который представляет шаблон npm Configuration file:

NPM and Angular in ASP.NET Core

Изменим содержимое этого файла следующим образом:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "author": "Eugene Popov <metanit.com>",
  "scripts": {
    "ng": "ng",
    "start": "echo hello && ng serve",
    "build": "ng build"
  },
  "dependencies": {
    "@angular/common": "~10.0.0",
    "@angular/compiler": "~10.0.0",
    "@angular/core": "~10.0.0",
    "@angular/forms": "~10.0.0",
    "@angular/platform-browser": "~10.0.0",
    "@angular/platform-browser-dynamic": "~10.0.0",
    "@angular/router": "~10.0.0",
    "rxjs": "~6.5.5",
    "zone.js": "~0.10.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.1000.0",
    "@angular/cli": "~10.0.0",
    "@angular/compiler-cli": "~10.0.0",
    "@types/node": "~14.0.14",
    "typescript": "~3.9.5"
  }
}

Здесь определены все необходимые пакеты фреймворка Angular, а также пакеты, которые помогут при разработке, в частности, TypeScript и Webpack.

Стоит отметить определение команды "start" в секции scripts: "start": "echo hello && ng serve". Выполнение команды "echo" перед командой "ng serve", которая запускает сервер с приложением Angular, - это своего рода хак для решения проблемы, описанной в https://github.com/dotnet/aspnetcore/issues/17277. Но в будущем, надеюсь, Microsoft исправит подобную ситуацию, и подобный зак больше не понадобится.

Также для избежания подобной ситуации я рекоммендую при отладке запускать проект по протоколу http, а не https.

Итак, сохраним файл и установим пакеты. Для этого в Visual Studio перейдем к окну Package Manager Console. (Например, через пункт меню Tools -> NuGet Package Manager -> Package Manager Console).

В этом окне сначала перейдем к папке, где находится файл package.json с помощью команды cd. Поскольку в моем случае проект называется HelloAngularApp и соответственно располагается в папке HelloAngularApp, а файл package.json в рамках проекта располагается в папке ClientApp, то я ввожу команду

cd HelloAngularApp/ClientApp

Далее введем команду

npm install
npm install и загрузка пакетов в Visual Studio в Angular

Также добавим в проект файл конфигурации TypeScript tsconfig.json:

TypeScript в Angular и ASP.NET Core

После добавления изменим содержимое файла следущим образом:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "files": [
    "src/main.ts",
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.d.ts"
  ]
}

Далее в каталоге ClientApp создадим новую папку src, которая будет хранить файлы приложения angular. В этой папке создадим каталог app, в который добавим новый файл app.component.ts:

Создание приложения Angular и ASP.NET Core

В файле app.component.ts определим код главного компонента приложения:

import { Component } from '@angular/core';
     
@Component({
    selector: 'app',
    template: `<label>Введите имя:</label>
                 <input [(ngModel)]="name" placeholder="name">
                 <h2>Добро пожаловать {{name}}!</h2>`
})
export class AppComponent { 
    name= '';
}

Также в папку ClientApp/src/app добавим новый файл app.module.ts, который будет представлять главный модуль приложения:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule }   from '@angular/forms';
import { AppComponent }   from './app.component';
@NgModule({
    imports:      [ BrowserModule, FormsModule ],
    declarations: [ AppComponent ],
    bootstrap:    [ AppComponent ]
})
export class AppModule { }

Далее в папку ClientApp/src добавим новый файл main.ts, с которого будет начинатся загрузка приложения:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app/app.module';

enableProdMode();
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Кроме собственно загрузки приложения данный файл определяет код для перезапуска приложения с помощью Hot Module при изменениях в файлах с исходным кодом.

И также в папку ClientApp/src добавим файл polyfills.ts:

import 'zone.js/dist/zone';

И также в папку ClientApp добавим новый json-файл angular.json

Angular CLI in ASP.NET Core

который будет определять конфигурацию Angular Cli:

{
  "version": 1,
  "projects": {
    "helloangularapp": {
      "projectType": "application",
      "root": "",
      "sourceRoot": "src",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.json",
            "aot": true
          },
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "helloangularapp:build"
          }
        }
      }
    }
  },
  "defaultProject": "helloangularapp"
}

Таким образом, все файлы будут компилироваться в две сборки - app.js и polyfills.js, которые будут располагаться в папке wwwroot/dist.

Теперь добавим в проект папку ClientApp/src главную веб-страницу приложения - файл index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Angular in ASP.NET Core</title>
    <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
    <app>Загрузка...</app>
</body>
</html>

В итоге весь проект будет выглядеть следующим образом:

Первый проект Angular в ASP.NET Core

И в конце изменим файл Startup.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;

namespace HelloAngularApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            if (!env.IsDevelopment())
            {
                app.UseSpaStaticFiles();
            }

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

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

Прежде всего в методе ConfigureServices() вызывается метод AddSpaStaticFiles(), который регистрирует сервис ISpaStaticFileProvider. Данный сервим позволяет обрабатывать статические файлы приложения Angular из определенного местоположения. Расположение файлов задается с помощью параметра configuration.RootPath. В данном случае это каталог ClientApp/dist, куда, как мы указали в файле angular.json, должны помещаться скомпилированные файлы. То есть данный сервис необходим нам, когда разработка завершена, файлы Angular на typescript скомпилированы в файлы javascript, а все приложение ASP.NET Core уже развернуто на сервере в режиме production.

Далее в методе Configure() добавляются соответствующие middleware, которые позволяют работать с приложением Angular.

Во-первых, если разработка закончена и приложение уже в состоянии развертывания, вызывается метод UseSpaStaticFiles(), который позволяет направлять запросы к нашему приложению Angular.

Далее вызывается метод app.UseSpa(), который позволяет в ответ на запросы отправлять веб-страницу по умолчанию (index.html). Этот middleware должен помещаться в конце конвейера.

В методе app.UseSpa(), если приложение находится в режиме разработки, в конвейер обработки запроса добавляется компонент spa.UseAngularCliServer. Этот компонент middleware позволяет настроить проект таким образом, что веб-сервер для приложения ASP.NET Core автоматически запускает сервер Angular CLI. Кроме того, UseAngularCliServer устанавливает прокси между сервером приложения ASP.NET Core и сервером приложения Angular. В качестве параметра метод принимает запускаемую команду из package.json. В данном случае это команда "start" ("echo hello && ng serve"), которая собственно и запускает веб-сервер Angular CLI.

То есть когда мы запустим проект ASP.NET Core на выполнение, фактически будут запущены два сервера с двумя приложениями, которые через прокси будут обмениваться данными. Если мы изменим код в файлах Angular, то моментально произойдет перекомпиляция файлов на typescript, и мы увидим результаты наших изменений в браузере. Однако если мы изменим код приложения ASP.NET Core (то есть код C#), тогда будет потребуется перекомпиляция и перезапуск всего приложения ASP.NET Core, что также перезапустит сервер с приложением Angular. Что, в конечном итоге, приведет к увеличению времени запуска и некоторому замедлению процесса разработки. Собственно это основной минус UseAngularCliServer. Хотя можно запускать отдельно оба сервера вручную.

Запустим проект на выполнение, в итоге Angular CLI скомпилирует приложение, с которым мы сможем взаимодействовать в рамках приложения ASP.NET Core:

Первое приложение на Angular в ASP.NET Core

Настройка публикации проекта

Таким образом, мы можем совместить разработку приложения Angular и ASP.NET Core. Но, кроме этого, нам надо установить настроить конфигурацию проекта, чтобы при его публикации также были скомпилированы и включены в выходной пакет приложения ASP.NET Core файлы приложения Angular. Для этого отредактируем файл проекта ASP.NET Core. Например, в моем случае сейчас он выглядит следущим образом:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="ClientApp\src\app\app.component.ts" />
    <None Remove="ClientApp\src\app\app.module.ts" />
    <None Remove="ClientApp\src\main.ts" />
    <None Remove="ClientApp\src\polyfills.ts" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.5" />
  </ItemGroup>

  <ItemGroup>
    <TypeScriptCompile Include="ClientApp\src\app\app.component.ts" />
    <TypeScriptCompile Include="ClientApp\src\app\app.module.ts" />
    <TypeScriptCompile Include="ClientApp\src\main.ts" />
    <TypeScriptCompile Include="ClientApp\src\polyfills.ts" />
  </ItemGroup>

</Project>

Изменим его следующим образом:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <SpaRoot>ClientApp\</SpaRoot>
  </PropertyGroup>

  <ItemGroup>
    <Content Remove="$(SpaRoot)**" />
    <None Remove="$(SpaRoot)**" />
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.5" />
  </ItemGroup>

  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />

    <ItemGroup>
      <DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>
</Project>

Теперь в узле PropertyGroup с помощью элемента SpaRoot устанавливается корневой каталог приложения Angular. То есть в данном случае это каталог ClientApp. С помощью узла Target устанавливаются настройки публикации. Например, элемент

<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />

будет запускать команду "npm run build -- --prod", которая будет компилировать приложение Angular.

Таким образом, после публикации в папке скомпилированного приложения мы сможем найти скомпилированные файлы приложения Angular:

Публикация приложения Angular в ASP.NET Core
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850