Поделиться через


Включите проверку подлинности в собственном приложении Angular с помощью Azure Active Directory B2C

В этой статье показано, как добавить проверку подлинности Azure Active Directory B2C (Azure AD B2C) в собственное одностраничное приложение Angular (SPA). Узнайте, как интегрировать приложение Angular с библиотекой аутентификации MSAL для Angular.

Используйте эту статью вместе со статьей по теме Настройка аутентификации в примере одностраничного приложения Angular. Замените образец приложения Angular собственным приложением Angular. После выполнения действий, описанных в этой статье, ваше приложение будет принимать вход через Azure AD B2C.

Предварительные требования

Выполните действия, описанные в статье Настройка проверки подлинности в примере Angular одностраничного приложения.

Создание проекта приложения Angular

Вы можете использовать существующий проект приложения Angular или создать новый. Чтобы создать проект, выполните следующие команды.

Команды

  1. Установите Angular CLI с помощью диспетчера пакетов npm.
  2. Создайте рабочее пространство Angular с модулем маршрутизации. Название приложения msal-angular-tutorial. Вы можете изменить его на любое допустимое имя приложения Angular, например contoso-car-service.
  3. Измените папку на каталог приложения.
npm install -g @angular/cli 
ng new msal-angular-tutorial --routing=true --style=css --strict=false
cd msal-angular-tutorial

Установка зависимостей

Чтобы установить в своем приложении Браузер MSAL и библиотеки MSAL Angular, выполните в командной оболочке следующую команду.

npm install @azure/msal-browser @azure/msal-angular

Установите библиотеку компонентов материалов Angular (необязательно для пользовательского интерфейса).

npm install @angular/material @angular/cdk

Добавление компонентов проверки подлинности

Пример кода состоит из следующих компонентов.

Компонент Тип Описание
auth-config.ts Константы Этот файл конфигурации содержит информацию о вашем поставщике удостоверений Azure AD B2C и службе веб-API. Приложение Angular использует эту информацию для установления доверительных отношений с Azure AD B2C, входа и выхода пользователя, получения и проверки токенов.
app.module.ts Модуль Angular Этот компонент описывает, как части приложения сочетаются друг с другом. Это корневой модуль, который используется для начальной загрузки и открытия приложения. В этом пошаговом руководстве вы добавляете некоторые компоненты в модуль app.module.ts и запускаете библиотеку MSAL с объектом конфигурации MSAL.
app-routing.module.ts Модуль маршрутизации Angular Этот компонент обеспечивает навигацию, интерпретируя URL-адрес браузера и загружая соответствующий компонент. В этом пошаговом руководстве вы добавляете некоторые компоненты в модуль маршрутизации и защищаете компоненты с помощью MSAL Guard. Доступ к защищенным компонентам могут получить только полномочные пользователи.
app.component.* Компонент Angular Команда ng new создала проект Angular с корневым компонентом. В этом пошаговом руководстве вы измените компонент приложения для размещения верхней панели навигации. Панель навигации содержит различные кнопки, включая кнопки входа и выхода. Класс app.component.ts обрабатывает события входа и выхода.
home.component.* Компонент Angular В этом пошаговом руководстве вы добавляете домашний компонент для отображения домашней страницы для анонимного доступа. Этот компонент показывает, как проверить, выполнил ли пользователь вход.
profile.component.* Компонент Angular В этом пошаговом руководстве вам предстоит добавить компонент profile, чтобы узнать, как читать утверждения маркера идентификации.
webapi.component.* Компонент Angular В этом пошаговом руководстве вам предстоит добавить компонент webapi, чтобы узнать, как вызвать веб-API.

Чтобы добавить в приложение следующие компоненты, выполните следующие команды интерфейса командной строки Angular. Команды generate component:

  1. Создайте папку для каждого компонента. В папке содержатся файлы TypeScript, HTML, CSS и тестовые файлы.
  2. Обновите файлы app.module.ts и app-routing.module.ts со ссылками на новые компоненты.
ng generate component home
ng generate component profile
ng generate component webapi

Добавление параметров приложения

Параметры поставщика удостоверений Azure AD B2C и веб-API хранятся в файле auth-config.ts. В папке src/app создайте файл с именем auth-config.ts, содержащий следующий код. Затем измените настройки, как описано в разделе 3.1, "Настройка примера Angular".

import { LogLevel, Configuration, BrowserCacheLocation } from '@azure/msal-browser';

const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1;
 
export const b2cPolicies = {
     names: {
         signUpSignIn: "b2c_1_susi_reset_v2",
         editProfile: "b2c_1_edit_profile_v2"
     },
     authorities: {
         signUpSignIn: {
             authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_susi_reset_v2",
         },
         editProfile: {
             authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_edit_profile_v2"
         }
     },
     authorityDomain: "your-tenant-name.b2clogin.com"
 };
 
 
export const msalConfig: Configuration = {
     auth: {
         clientId: '<your-MyApp-application-ID>',
         authority: b2cPolicies.authorities.signUpSignIn.authority,
         knownAuthorities: [b2cPolicies.authorityDomain],
         redirectUri: '/', 
     },
     cache: {
         cacheLocation: BrowserCacheLocation.LocalStorage,
         storeAuthStateInCookie: isIE, 
     },
     system: {
         loggerOptions: {
            loggerCallback: (logLevel, message, containsPii) => {
                console.log(message);
             },
             logLevel: LogLevel.Verbose,
             piiLoggingEnabled: false
         }
     }
 }

export const protectedResources = {
  todoListApi: {
    endpoint: "http://localhost:5000/api/todolist",
    scopes: ["https://your-tenant-name.onmicrosoft.com/api/tasks.read"],
  },
}
export const loginRequest = {
  scopes: []
};

Запустите библиотеки аутентификации

Общедоступные клиентские приложения не являются доверенными для безопасного хранения секретов приложений, поэтому у них нет секретов клиента. В папке src/app откройте файл app.module.ts и внесите следующие изменения.

  1. Импортируйте библиотеки MSAL Angular и браузер MSAL.
  2. Импортируйте модуль конфигурации Azure AD B2C.
  3. Импорт HttpClientModule. Для вызова веб-API используется клиент HTTP.
  4. Импортируйте HTTP-перехватчик Angular. MSAL использует перехватчик для вставки токена носителя в заголовок авторизации HTTP.
  5. Добавьте необходимые материалы Angular.
  6. Создайте экземпляр MSAL, используя объект общедоступного клиентского приложения с несколькими учетными записями. Инициализация MSAL включает в себя передачу:
    1. Объект конфигурации для auth-config.ts.
    2. Объект конфигурации для защиты маршрутизации.
    3. Объект конфигурации для перехватчика MSAL. Класс перехватчика автоматически получает токены для исходящих запросов, которые используют класс Angular HttpClient к известным защищенным ресурсам.
  7. Настройте поставщики AngularHTTP_INTERCEPTORS и MsalGuard.
  8. Добавьте MsalRedirectComponent в начальную загрузку Angular.

В папке src/app отредактируйте app.module.ts и внесите изменения, показанные в следующем фрагменте кода. Изменения отмечены флажками "Изменения начинаются здесь" и "Изменения здесь заканчиваются".

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

/* Changes start here. */
// Import MSAL and MSAL browser libraries. 
import { MsalGuard, MsalInterceptor, MsalModule, MsalRedirectComponent } from '@azure/msal-angular';
import { InteractionType, PublicClientApplication } from '@azure/msal-browser';

// Import the Azure AD B2C configuration 
import { msalConfig, protectedResources } from './auth-config';

// Import the Angular HTTP interceptor. 
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { ProfileComponent } from './profile/profile.component';
import { HomeComponent } from './home/home.component';
import { WebapiComponent } from './webapi/webapi.component';

// Add the essential Angular materials.
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';
import { MatTableModule } from '@angular/material/table';
/* Changes end here. */

@NgModule({
  declarations: [
    AppComponent,
    ProfileComponent,
    HomeComponent,
    WebapiComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    /* Changes start here. */
    // Import the following Angular materials. 
    MatButtonModule,
    MatToolbarModule,
    MatListModule,
    MatTableModule,
    // Import the HTTP client. 
    HttpClientModule,

    // Initiate the MSAL library with the MSAL configuration object
    MsalModule.forRoot(new PublicClientApplication(msalConfig),
      {
        // The routing guard configuration. 
        interactionType: InteractionType.Redirect,
        authRequest: {
          scopes: protectedResources.todoListApi.scopes
        }
      },
      {
        // MSAL interceptor configuration.
        // The protected resource mapping maps your web API with the corresponding app scopes. If your code needs to call another web API, add the URI mapping here.
        interactionType: InteractionType.Redirect,
        protectedResourceMap: new Map([
          [protectedResources.todoListApi.endpoint, protectedResources.todoListApi.scopes]
        ])
      })
    /* Changes end here. */
  ],
  providers: [
    /* Changes start here. */
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    },
    MsalGuard
    /* Changes end here. */
  ],
  bootstrap: [
    AppComponent,
    /* Changes start here. */
    MsalRedirectComponent
    /* Changes end here. */
  ]
})
export class AppModule { }

Настройка маршрутов

В этом разделе вам предстоит настроить маршруты для приложения Angular. Когда пользователь выбирает ссылку на странице для перемещения в вашем одностраничном приложении или вводит URL-адрес в адресной строке, маршруты сопоставляют URL-адрес с компонентом Angular. Интерфейс canActivate маршрутизации Angular использует MSAL Guard, чтобы проверить, вошел ли пользователь в систему. Если пользователь не вошел в систему, MSAL переводит его в Azure AD B2C для проверки подлинности.

В папке src/app отредактируйте app-routing.module.ts, внесите изменения, показанные в следующем фрагменте кода. Изменения отмечены флажками "Изменения начинаются здесь" и "Изменения здесь заканчиваются".

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MsalGuard } from '@azure/msal-angular';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
import { WebapiComponent } from './webapi/webapi.component';

const routes: Routes = [
  /* Changes start here. */
  {
    path: 'profile',
    component: ProfileComponent,
    // The profile component is protected with MSAL Guard.
    canActivate: [MsalGuard]
  },
  {
    path: 'webapi',
    component: WebapiComponent,
    // The profile component is protected with MSAL Guard.
    canActivate: [MsalGuard]
  },
  {
    // The home component allows anonymous access
    path: '',
    component: HomeComponent
  }
  /* Changes end here. */
];


@NgModule({
  /* Changes start here. */
  // Replace the following line with the next one
  //imports: [RouterModule.forRoot(routes)],
  imports: [RouterModule.forRoot(routes, {
    initialNavigation:'enabled'
  })],
  /* Changes end here. */
  exports: [RouterModule]
})
export class AppRoutingModule { }

Добавление кнопок входа и выхода

В этом разделе вы добавляете кнопки входа и выхода в компонент приложения. В папке src/app откройте файл app.component.ts и внесите следующие изменения.

  1. Импортируйте необходимые компоненты.

  2. Измените класс, чтобы реализовать метод OnInit. Метод OnInit подписывается на наблюдаемое событие inProgress$MSAL MsalBroadcastService. Это событие используется для получения сведений о состоянии взаимодействия с пользователем, особенно для проверки завершения взаимодействия.

    Перед взаимодействием с объектом учетной записи MSAL убедитесь, что свойство InteractionStatus возвращает значение InteractionStatus.None. Событие subscribe вызывает метод setLoginDisplay, чтобы проверить, прошел ли пользователь проверку подлинности.

  3. Добавьте переменные класса.

  4. Добавьте метод login, запускающий поток авторизации.

  5. Добавьте метод logout, который выполняет выход пользователя из системы.

  6. Добавьте метод setLoginDisplay, который проверяет, прошел ли пользователь проверку подлинности.

  7. Добавьте метод ngOnDestroy для очистки события подписки inProgress$.

После изменения код должен выглядеть, как показано в следующем фрагменте кода.

import { Component, OnInit, Inject } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

/* Changes start here. */
export class AppComponent implements OnInit{
  title = 'msal-angular-tutorial';
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();

  constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }

  ngOnInit() {

    this.broadcastService.inProgress$
    .pipe(
      filter((status: InteractionStatus) => status === InteractionStatus.None),
      takeUntil(this._destroying$)
    )
    .subscribe(() => {
      this.setLoginDisplay();
    })
  }

  login() {
    if (this.msalGuardConfig.authRequest){
      this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
    } else {
      this.authService.loginRedirect();
    }
  }

  logout() { 
    this.authService.logoutRedirect({
      postLogoutRedirectUri: 'http://localhost:4200'
    });
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
  /* Changes end here. */
}

В папке src/app отредактируйте app.component.html и внесите следующие изменения.

  1. Добавьте ссылку на профиль и компоненты веб-API.
  2. Добавьте кнопку входа в систему с атрибутом события щелчка, установленным для метода login(). Эта кнопка появляется только в том случае, если переменной класса loginDisplay является false.
  3. Добавьте кнопку выхода с атрибутом события щелчка, установленным для метода logout(). Эта кнопка появляется только в том случае, если переменной класса loginDisplay является true.
  4. Добавьте элемент router-outlet.

После изменения код должен выглядеть, как показано в следующем фрагменте кода.

<mat-toolbar color="primary">
  <a class="title" href="/">{{ title }}</a>

  <div class="toolbar-spacer"></div>

  <a mat-button [routerLink]="['profile']">Profile</a>
  <a mat-button [routerLink]="['webapi']">Web API</a>

  <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
  <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button>

</mat-toolbar>
<div class="container">
  <router-outlet></router-outlet>
</div>

При желании обновите файл app.component.css следующим фрагментом CSS.

.toolbar-spacer {
    flex: 1 1 auto;
  }

  a.title {
    color: white;
  }

Обработка перенаправлений приложения

Когда вы используете перенаправления с MSAL, вы должны добавить директиву app-redirect в index.html. В папке src отредактируйте index.html, как показано в следующем фрагменте кода.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MsalAngularTutorial</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
  <!-- Changes start here -->
  <app-redirect></app-redirect>
  <!-- Changes end here -->
</body>
</html>

Задание CSS для приложения (необязательно)

В папке /src обновите файл styles.css следующим фрагментом CSS.

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
.container { margin: 1%; }

Совет

На этом этапе вы можете запустить приложение и протестировать вход в систему. Чтобы запустить приложение, см. раздел Запуск приложения Angular.

Проверка аутентификации пользователя

Файл home.component демонстрирует, как проверить, аутентифицирован ли пользователь. В папке src/app/home обновите home.component.ts с помощью следующего фрагмента кода.

Этот код:

  1. Подписывается на наблюдаемые события msalSubject$ и inProgress$MsalBroadcastService MSAL.
  2. Гарантирует, что событие msalSubject$ записывает результат аутентификации в консоль браузера.
  3. Гарантирует, что событие inProgress$ проверяет, аутентифицирован ли пользователь. Метод getAllAccounts() возвращает один объект или несколько.
import { Component, OnInit } from '@angular/core';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
import { filter } from 'rxjs/operators';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  loginDisplay = false;

  constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }

  ngOnInit(): void {
    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
      )
      .subscribe((result: EventMessage) => {
        console.log(result);
      });

    this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) => status === InteractionStatus.None)
      )
      .subscribe(() => {
        this.setLoginDisplay();
      })
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }
}

Добавьте в файл home.component.html в папке src/app/home следующий фрагмент кода HTML. Директива *ngIf проверяет, показывает ли переменная класса loginDisplay приветственные сообщения или скрывает их.

<div *ngIf="!loginDisplay">
    <p>Please sign-in to see your profile information.</p>
</div>

<div *ngIf="loginDisplay">
    <p>Login successful!</p>
    <p>Request your profile information by clicking Profile above.</p>
</div>

Чтение утверждений маркера идентификатора

Файл profile.component демонстрирует, как получить доступ к утверждениям токена идентификатора пользователя. В папке src/app/profile обновите profile.component.ts с помощью следующего фрагмента кода.

Этот код:

  1. импортирует необходимые компоненты;
  2. подписывается на наблюдаемое событие inProgress$MsalBroadcastService MSAL. Событие загружает учетную запись и считывает утверждения токена идентификатора.
  3. Гарантирует, что метод checkAndSetActiveAccount проверяет и устанавливает активную учетную запись. Это действие часто встречается, когда приложение взаимодействует с несколькими пользовательскими потоками Azure AD B2C или настраиваемыми политиками.
  4. Гарантирует, что метод getClaims получает утверждения токена идентификатора от активного объекта учетной записи MSAL. Затем метод добавляет утверждения в массив dataSource. Массив отрисовывается для пользователя с помощью привязки к шаблону компонента.
import { Component, OnInit } from '@angular/core';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})

export class ProfileComponent implements OnInit {
  displayedColumns: string[] = ['claim', 'value'];
  dataSource: Claim[] = [];
  private readonly _destroying$ = new Subject<void>();
  
  constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }

  ngOnInit(): void {

    this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) =>  status === InteractionStatus.None || status === InteractionStatus.HandleRedirect),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        this.checkAndSetActiveAccount();
        this.getClaims(this.authService.instance.getActiveAccount()?.idTokenClaims)
      })
  }

  checkAndSetActiveAccount() {

    let activeAccount = this.authService.instance.getActiveAccount();

    if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
      let accounts = this.authService.instance.getAllAccounts();
      this.authService.instance.setActiveAccount(accounts[0]);
    }
  }

  getClaims(claims: any) {

    let list: Claim[]  =  new Array<Claim>();

    Object.keys(claims).forEach(function(k, v){
      
      let c = new Claim()
      c.id = v;
      c.claim = k;
      c.value =  claims ? claims[k]: null;
      list.push(c);
    });
    this.dataSource = list;

  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
}

export class Claim {
  id: number;
  claim: string;
  value: string;
}

В папке src/app/profile обновите profile.component.html следующим фрагментом HTML.

<h1>ID token claims:</h1>

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

  <!-- Claim Column -->
  <ng-container matColumnDef="claim">
    <th mat-header-cell *matHeaderCellDef> Claim </th>
    <td mat-cell *matCellDef="let element"> {{element.claim}} </td>
  </ng-container>

  <!-- Value Column -->
  <ng-container matColumnDef="value">
    <th mat-header-cell *matHeaderCellDef> Value </th>
    <td mat-cell *matCellDef="let element"> {{element.value}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

Вызов веб-API

Для вызова веб-API авторизации на основе токена приложению требуется действительный маркер доступа. Поставщик MsalInterceptor автоматически получает токены для исходящих запросов, которые используют класс Angular HttpClient для известных защищенных ресурсов.

Важно!

Метод инициализации MSAL (в классе app.module.ts) сопоставляет защищенные ресурсы, такие как веб-API, с требуемыми областями действия приложения с помощью объекта protectedResourceMap. Если вашему коду необходимо вызвать другой веб-API, добавьте URI веб-API и HTTP-метод веб-API с соответствующими областями действия к объекту protectedResourceMap. Для получения дополнительной информации см. защищенную карту ресурсов.

Когда объект HttpClient вызывает веб-API, поставщик MsalInterceptor выполняет следующие действия:

  1. получает маркер доступа с необходимыми разрешениями (областями) для конечной точки веб-API;

  2. Передает токен доступа как токен-носитель в заголовке авторизации HTTP-запроса, используя этот формат.

    Authorization: Bearer <access-token>
    

Файл webapi.component демонстрирует, как вызвать веб-API. В папке src/app/webapi обновите webapi.component.ts с помощью следующего фрагмента кода.

Этот код:

  1. Использует класс Angular HttpClient для вызова веб-API.
  2. Читает в классе auth-config элемент protectedResources.todoListApi.endpoint. Этот элемент указывает URI веб-API. На основе URI веб-API перехватчик MSAL получает маркер доступа с соответствующими областями.
  3. Получает профиль из веб-API и задает переменную класса profile.
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { protectedResources } from '../auth-config';

type ProfileType = {
  name?: string
};

@Component({
  selector: 'app-webapi',
  templateUrl: './webapi.component.html',
  styleUrls: ['./webapi.component.css']
})
export class WebapiComponent implements OnInit {
  todoListEndpoint: string = protectedResources.todoListApi.endpoint;
  profile!: ProfileType;

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit() {
    this.getProfile();
  }

  getProfile() {
    this.http.get(this.todoListEndpoint)
      .subscribe(profile => {
        this.profile = profile;
      });
  }
}

Добавьте в файл webapi.component.html в папке src/app/webapi следующий фрагмент кода HTML. Шаблон компонента отображает имя, возвращаемое веб-API. В нижней части страницы шаблон отрисовывает адрес веб-API.

<h1>The web API returns:</h1>
<div>
    <p><strong>Name: </strong> {{profile?.name}}</p>
</div>

<div class="footer-text">
    Web API: {{todoListEndpoint}}
</div>

При желании обновите файл webapi.component.css следующим фрагментом CSS.

.footer-text {
    position: absolute;
    bottom: 50px;
    color: gray;
}

Запуск приложения Angular

Выполните следующую команду:

npm start

В окне консоли отображается номер порта, на котором размещено приложение.

Listening on port 4200...

Совет

Кроме того, для выполнения команды npm start можно использовать отладчик Visual Studio Code. Отладчик помогает ускорить цикл редактирования, компиляции и отладки.

Перейдите к http://localhost:4200 в браузере для просмотра приложения.

Дальнейшие действия