This repo contains a sample application created with Visual Studio 2019 using .NET Core 2.2 and Angular 7.
The Web API for the application comes from the JavaScript Single Page Application with an ASP.NET backend, using msal.js.
IMPORTANT
If you don't already have it, I highly recommend Mads Kristensen's Open Command Line Visual Studio Extension, which I use to open a command line for all of the work done in building this sample.
See _initialSetup/_initialSetupReadme.md and follow the instructions there to copy the files authconfig-dev-local.ts
and authconfig-prod.ts
from _initialSetup\alt-auth
in the root of this solution to
TodoSPA\ClientApp\src\app-alt\shared\auth
.
You will see additional instructions below in this readme about setting the values in those files.
You will also need the library Microsoft.Identity.Web.
Follow this link to see how to build the library for use with this sample.
Quick information on environment.
node --version
v10.16.0
npm --version
6.9.0
Start Visual Studio 2019 and see the dialog below:
Click on "Create a new project", or if the above dialog was dismissed, choose File -- New Project.
Filter by Project type Web
and then click on ASP.Net Core Web Application
and click Next
.
Set the Project name, Location and Solution name and click Create
.
Choose Angular and click Create
.
Now build and run the application.
Check global npm packages:
npm list -g --depth 0
got
C:\ProgramData\npm
+-- @angular/[email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
ng update
got
We analyzed your package.json, there are some packages to update:
Name Version Command to update
---------------------------------------------------------------------------------------
@angular/cli 6.0.8 -> 8.0.3 ng update @angular/cli
@angular/core 6.1.10 -> 8.0.2 ng update @angular/core
@angular/core 6.1.10 -> 7.2.15 ng update @angular/core
@nguniversal/aspnetcore-engine 6.0.0 -> 7.1.1 ng update @nguniversal/aspnetcore-engine
rxjs 6.2.1 -> 6.5.2 ng update rxjs
There might be additional packages that are outdated.
Or run ng update --all to try to update all at the same time.
With Angular CLI v7.3.8 installed globally, and version specified in package.json at ~6.0.0, to update to 7.3.8 for project:
ng update @angular/cli@7.3.8
Updated @angular/cli
in package.json
.
ng update @angular/core@7.2.15
updated multiple packages in package.json
.
ng update
got
We analyzed your package.json, there are some packages to update:
Name Version Command to update
---------------------------------------------------------------------------------------
@angular/cli 7.3.9 -> 8.0.3 ng update @angular/cli
@angular/core 7.2.15 -> 8.0.2 ng update @angular/core
@nguniversal/aspnetcore-engine 6.0.0 -> 7.1.1 ng update @nguniversal/aspnetcore-engine
There might be additional packages that are outdated.
Run "ng update --all" to try to update all at the same time.
Compiled and ran and app runs successfully.
After looking at a number of Todo application samples, the Tutorial: Create a web API with ASP.NET Core looked to be the best to use.
Copied files from the AspNetCore.Docs samples - 2.2 TodoApi
Controllers\TodoController.cs
Models\TodoContent.cs
TodoItem.cs
Changed namespace from TodoApi
to TodoSPA
.
Added the following in Startup.cs:
using Microsoft.EntityFrameworkCore;
//...
using TodoSPA.Models;
and
services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
Compiled app and tried API url: https://localhost:44358/api/todo
.
Got:
[{"id":1,"name":"Item1","isComplete":false}]
Adapted code from \microsoft-authentication-library-for-js\lib\msal-angular\samples\MSALAngularDemoApp\src\todo-list
so it could be used with the API sample.
File in src\app\common
HttpServiceHelper.ts
Files in src\todo-list:
todo-list.component.css
todo-list.component.html
todo-list.component.spec.ts
todo-list.component.ts
todo-list.service.ts
todoList.ts
The object model of the C# Web API sample uses the property Name
where the demo sample code for the MSAL demo uses the property Title
.
Changes from sample repo:
import {Observable} from 'rxjs/Rx'
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
@Injectable()
export class HttpServiceHelper {
constructor(private http: HttpClient) {
}
public httpGetRequest(url : string) {
return this.http.get(url)
.map(response => {
return response;
})
.catch(response => (Observable.throw(response)
))
}
}
to
import { Observable } from 'rxjs';
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { map, catchError } from 'rxjs/operators';
@Injectable()
export class HttpServiceHelper {
constructor(private http: HttpClient) {
}
public httpGetRequest(url : string) {
return this.http.get(url)
.pipe(map(response => {
console.log("http response");
return response;
}),
catchError(response => (Observable.throw(response)
)))
}
}
<td>{{item?.title}}</td>
changed to
<td>{{item?.name}}</td>
Updates to rxjs, commented out msal related code for now.
import {Subscription} from "rxjs/Subscription";
import {BroadcastService} from "@azure/msal-angular";
import { MsalService} from "@azure/msal-angular";
to
import {Subscription} from "rxjs";
//import {BroadcastService} from "@azure/msal-angular";
//import { MsalService} from "@azure/msal-angular";
...
constructor(private todoListService: TodoListService, private broadcastService : BroadcastService, private msalService: MsalService) { }
to
constructor(private todoListService: TodoListService) { } //, private broadcastService : BroadcastService, private msalService: MsalService) { }
...
'title': this.newTodoCaption,
to
'name': this.newTodoCaption,
And commented out lines 26-19, 38-41, 44-46 and 80.
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs/Rx";
import {TodoList} from "./todoList";
@Injectable()
export class TodoListService {
private apiEndpoint: string = "https://buildtodoservice.azurewebsites.net/api/todolist";
constructor(private http: HttpClient) {
}
getItems(): Observable<TodoList[]> {
return this.http.get(this.apiEndpoint)
.map((response: Response) =>
response
)
.catch(response => (Observable.throw(response)
))
}
postItem(item: any) {
return this.http.post(this.apiEndpoint, item, {responseType: 'text'})
.map((response) => {
return response;
})
}
}
to
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {TodoList} from "./todoList";
import { map, catchError } from 'rxjs/operators';
@Injectable()
export class TodoListService {
private apiEndpoint: string = "https://localhost:44358/api/todo";
constructor(private http: HttpClient) {
}
getItems(): Observable<TodoList[]> {
return this.http.get(this.apiEndpoint)
.pipe(map((response: TodoList[]) => response
),
catchError(response => (Observable.throw(response))))
}
postItem(item: any) {
return this.http.post(this.apiEndpoint, item, {responseType: 'text'})
.pipe(map((response) => {
return response;
}))
}
}
export class TodoList {
constructor(title:string, owner: string) {
this.title=title;
this.owner= owner;
}
title:string;
owner: string;
}
to
export class TodoList {
constructor(name:string, owner: string) {
this.name = name;
this.owner= owner;
}
name:string;
owner: string;
}
Added the following as a new li
:
<li class="nav-item" [routerLinkActive]='["link-active"]'>
<a class="nav-link text-dark" [routerLink]='["/todo-list"]'>Todo List</a>
</li>
Added imports:
import { TodoListComponent } from "./todo-list/todo-list.component";
import { TodoListService } from "./todo-list/todo-list.service";
Changed declarations:
FetchDataComponent
to
FetchDataComponent,
TodoListComponent
added to RouterModule.forRoot:
{ path: 'todo-list', component: TodoListComponent },
changed providers:
providers: [],
to
providers: [TodoListService],
NOTES INCOMPLETE HERE - The notes on ADAL integration are not yet complete (c. late June 2019), though the source can be built and run with the notes here.
Note: If you are building your app from scratch (rather than using the code for this sample from GitHub), you will need to change the URL for your local application.
For the code in this repository, the URL is https://localhost:44358
.
I've used the name TodoSPA_sample_local
when registering in my AAD.
Added new folders shared
and shared\auth
under ClientApp\src\app
.
Added new file shared-config.models.ts
in folder ClientApp\src\app\shared
with the following content:
export interface AuthConfig {
authConfigType: string;
clientId: string;
}
Added new file authconfig.ts
in folder ClientApp\src\app\shared\auth
:
import { AuthConfig } from "../../../app/shared/shared-config.models";
//*** Important *** The values in this file in .../ClientApp/src/app/shared/auth
//will be replaced by the contents of a file from .../ClientApp/src/app-alt/shared/auth
//
// See readme.md from root of solution for more information
export const AUTH_CONFIG: AuthConfig = {
authConfigType: "empty",
clientId: "00000000-0000-0000-0000-000000000000"
}
IMPORTANT: Before building this app, see the notes in _initialSetup/_initialSetupReadme.md and copy files as directed there.
Replace a portion of ClientApp\angular.json
"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
}
}
with
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
},
{
"replace": "src/app/shared/auth/authconfig.ts",
"with": "src/app-alt/shared/auth/authconfig-prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
},
"dev-local": {
"fileReplacements": [
{
"replace": "src/app/shared/auth/authconfig.ts",
"with": "src/app-alt/shared/auth/authconfig-dev-local.ts"
}
]
}
}
Add to scripts
in package.json
:
"build:devlocal": "ng build --aot=true --configuration=dev-local",
Follow the instructions in the Quickstart to register your application.
Quickstart: Register an application with the Microsoft identity platform
[If not done yet, copy the files into TodoSPA\ClientApp\src\app-alt\shared\auth
as described earlier in this readme.]
See the following pages for additional resources
Azure Active Directory Resources
Additional resources: Resources