共用方式為


使用 Azure Active Directory B2C 在您自己的 Angular 應用程式中啟用驗證

本文顯示如何將 Azure Active Directory B2C (Azure AD B2C) 驗證新增至您自己的 Angular 單頁應用程式 (SPA)。 了解如何將 Angular 應用程式與適用於 Angular 的 MSAL 驗證程式庫整合。

請搭配使用本文與標題為在範例 Angular 單頁應用程式中設定驗證的相關文章。 將範例 Angular 應用程式替換成您自己的 Angular 應用程式。 當您完成本文中的步驟之後,您的應用程式將會透過 Azure AD B2C 接受登入。

必要條件

完成範例Angular單頁應用程式一文中設定驗證的步驟。

建立 Angular 應用程式專案

您可以使用現有的 Angular 應用程式專案,或建立新專案。 若要建立新專案,請執行下列命令。

命令:

  1. 使用 npm 套件管理員安裝 Angular CLI
  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 材質元件庫 (選用,針對 UI):

npm install @angular/material @angular/cdk

新增驗證元件

範例程式碼由下列元件組成:

元件 類型 Description
auth-config.ts 常數 此設定檔包含 Azure AD B2C 身分識別提供者和 Web 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 元件變更為裝載頂端導覽列。 導覽列包含各種按鈕,包括登入和登出按鈕。 app.component.ts 類別會處理登入和登出事件。
home.component.* Angular 元件 在此逐步解說中,您會新增 home 元件,以轉譯匿名存取的首頁。 此元件示範如何檢查使用者是否已登入。
profile.component.* Angular 元件 在此逐步解說中,您會新增 profile 元件,以了解如何讀取識別碼權杖宣告。
webapi.component.* Angular 元件 在此逐步解說中,您會新增 webapi 元件,以了解如何呼叫 Web API。

若要將下列元件新增至您的應用程式,請執行下列 Angular CLI 命令。 generate component 命令:

  1. 建立每個元件的資料夾。 此資料夾包含 TypeScript、HTML、CSS 和測試檔案。
  2. 使用新元件的參考來更新 app.module.tsapp-routing.module.ts 檔案。
ng generate component home
ng generate component profile
ng generate component webapi

新增應用程式設定

Azure AD B2C 身分識別提供者和 Web 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。 HTTP 用戶端用來呼叫 Web API。
  4. 匯入 Angular HTTP 攔截器。 MSAL 會使用攔截器將持有人權杖插入 HTTP 授權標頭。
  5. 新增必要的 Angular 材質。
  6. 使用多個帳戶公用用戶端應用程式物件來具現化 MSAL。 MSAL 初始化包括傳遞:
    1. auth-config.ts 的設定物件。
    2. 路由傳送防護的設定物件。
    3. MSAL 攔截器的設定物件。 攔截器類別會自動取得傳出要求的權杖,而此權杖使用已知受保護資源的 Angular HttpClient 類別。
  7. 設定 HTTP_INTERCEPTORSMsalGuardAngular 提供者
  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 元件。 Angular 路由傳送 canActivate 介面會使用 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 { }

新增登入和登出按鈕

在本節中,您會將登入和登出按鈕新增至 app 元件。 在 src/app 資料夾中,開啟 app.component.ts 檔案,並進行下列變更:

  1. 匯入必要元件。

  2. 變更類別以實作 OnInit 方法OnInit 方法會訂閱 MSAL MsalBroadcastServiceinProgress$ 可觀察事件。 使用此事件可知道使用者互動的狀態,特別是檢查是否已完成互動。

    在與 MSAL account 物件互動之前,請先檢查 InteractionStatus 屬性是否傳回 InteractionStatus.Nonesubscribe 事件會呼叫 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. 新增設定檔和 Web API 元件的連結。
  2. 新增 click 事件屬性設定為 login() 方法的登入按鈕。 只有在 loginDisplay 類別變數為 false 時,才會顯示此按鈕。
  3. 新增 click 事件屬性設定為 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>

選擇性地使用下列 CSS 程式碼片段來更新 app.component.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 資料夾中,使用下列 CSS 程式碼片段來更新 styles.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. 訂閱 MSAL MsalBroadcastServicemsalSubject$inProgress$ 可觀察事件。
  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;
  }
}

在 src/app/home 資料夾中,使用下列 HTML 程式碼片段來更新 home.component.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. 訂閱 MSAL MsalBroadcastServiceinProgress$ 可觀察事件。 此事件會載入帳戶,並讀取識別碼權杖宣告。
  3. 確保 checkAndSetActiveAccount 方法會檢查並設定使用中帳戶。 應用程式與多個 Azure AD B2C 使用者流程或自訂原則互動時,此動作十分常見。
  4. 確保 getClaims 方法會從使用中 MSAL account 物件取得識別碼權杖宣告。 此方法接著會將宣告新增至 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 資料夾中,使用下列 HTML 程式碼片段來更新 profile.component.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>

呼叫 Web API

若要呼叫權杖型授權 Web API,應用程式必須具備有效的存取權杖。 MsalInterceptor 提供者會自動取得傳出要求的權杖,而此權杖使用已知受保護資源的 Angular HttpClient 類別。

重要

MSAL 初始化方法 (在 app.module.ts 類別中) 會使用 protectedResourceMap 物件,以將受保護資源 (例如 Web API) 對應至必要應用程式範圍。 如果您的程式碼需要呼叫另一個 Web API,則請將 Web API URI 和 Web API HTTP 方法 (具有對應的範圍) 新增至 protectedResourceMap 物件。 如需詳細資訊,請參閱 受保護資源對應

HttpClient 物件呼叫 Web API 時,MsalInterceptor 提供者會採取下列步驟:

  1. 使用 Web API 端點的必要權限 (範圍) 取得存取權杖。

  2. 使用下列格式,在 HTTP 要求的授權標頭中,以持有人權杖的形式傳遞存取權杖:

    Authorization: Bearer <access-token>
    

webapi.component 檔案會示範如何呼叫 Web API。 在 src/app/webapi 資料夾中,使用下列程式碼片段來更新 webapi.component.ts。

程式碼:

  1. 使用 Angular HttpClient 類別來呼叫 Web API。
  2. 讀取 auth-config 類別的 protectedResources.todoListApi.endpoint 元素。 此元素會指定 Web API URI。 MSAL 攔截器會根據 Web API URI 來取得具有對應範圍的存取權杖。
  3. 從 Web 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;
      });
  }
}

在 src/app/webapi 資料夾中,使用下列 HTML 程式碼片段來更新 webapi.component.html。 元件的範本會轉譯 Web API 所傳回的名稱。 在頁面底部,範本會轉譯 Web API 位址。

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

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

選擇性地使用下列 CSS 程式碼片段來更新 webapi.component.css 檔案:

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

執行 Angular 應用程式

執行以下命令:

npm start

主控台視窗會顯示裝載應用程式的連接埠號碼。

Listening on port 4200...

提示

或者,若要執行 npm start 命令,請使用 Visual Studio Code 偵錯工具。 偵錯工具有助於加速您的編輯、編譯和偵錯迴圈。

在瀏覽器中移至 http://localhost:4200 可檢視應用程式。

後續步驟