Asp.net Core 2 Microsoft Account Is Experiencing Technical Problems. Please Try Again Later.

I've been thinking about writing a blog post since the first version of Angular practically killed Microsoft on the client side. Technologies similar ASP.Net, Spider web Forms, and MVC Razor have get obsolete, replaced past a JavaScript framework that's not exactly Microsoft. However, since the second version of Angular, Microsoft and Google take been working together to create Athwart 2, and this is when my two favorite technologies started working together.

In this weblog, I want to help people create the best architecture combining these two worlds. Are you ready? Here nosotros go!

Almost the architecture

You will build an Angular 5 client that consumes a RESTful Spider web API Core 2 service.

The client side:

  • Angular 5
  • Angular CLI
  • Angular Material

The server side:

  • .Internet C# Web API Cadre 2
  • Injection dependencies
  • JWT authentication
  • Entity framework code first
  • SQL Server

Note

In this blog post nosotros are assuming the reader already has basic knowledge of TypeScript, Angular modules, components, and importing/exporting. The goal of this post is to create a good compages that will allow for the lawmaking to grow over time.

What Exercise Yous Need?

Permit'south showtime by choosing the IDE. Of form, this is simply my preference, and you can use the one yous feel more comfortable with. In my case, I will use Visual Studio Code and Visual Studio 2017.

Why two different IDEs? Since Microsoft created Visual Studio Code for the front end end, I cannot cease using this IDE. Anyway, nosotros will besides see how to integrate Angular 5 inside the solution project, that volition help y'all if you lot are the kind of developer who prefers to debug both dorsum end and front end with only i F5.

About the dorsum finish, you can install the latest Visual Studio 2017 version which has a gratis edition for developers but is very complete: Community.

So, here the list of things we need to install for this tutorial:

  • Visual Studio Code
  • Visual Studio 2017 Customs (or Whatsoever)
  • Node.js v8.ten.0
  • SQL Server 2017

Notation

Verify that you are running at least Node half dozen.ix.x and npm 3.x.x by running node -five and npm -five in a terminal or console window. Older versions produce errors, but newer versions are fine.

The Forepart Cease

Quick Start

Permit the fun begin! The first thing nosotros need to do is install Angular CLI globally, so open the node.js command prompt and run this command:

          npm install -yard @athwart/cli                  

Okay, at present we accept our module bundler. This unremarkably installs the module nether your user folder. An alias should non be necessary past default, but if yous need information technology you tin execute the next line:

          alias ng="<UserFolder>/.npm/lib/node_modules/angular-cli/bin/ng"                  

The next stride is to create the new project. I will telephone call it angular5-app. Showtime, nosotros navigate to the folder under which we want to create the site, and then:

          ng new angular5-app                  

First Build

While you can test your new website but running ng serve --open, I practise recommend testing the site from your favorite web service. Why? Well, some issues can happen only in production, and building the site with ng build is the closest way to approach this environment. So we can open the folder angular5-app with Visual Studio Code and run ng build on the terminal bash:

building the angular app for the first time

A new folder chosen dist will be created and we tin can serve it using IIS or whichever web server yous adopt. And then you tin can type the URL in the browser, and…done!

the new directory structure

Note

It is not the purpose of this tutorial to show how to set upwards a web server, so I assume you already have that cognition.

Angular 5 Welcome Screen

The src Binder

The src folder structure

My src binder is structured every bit follows: Inside the app folder we take components where nosotros volition create for each Athwart component the css, ts, spec, and html files. We will also create a config folder to keep the site configuration, directives will have all our custom directives, helpers volition house common code like the authentication managing director, layout volition contain the chief components like torso, caput, and side panels, models keeps what will match with the back-cease view models, and finally services volition have the lawmaking for all the calls to the back stop.

Outside the app binder nosotros will go along the folders created past default, like assets and environments, and likewise the root files.

Creating the Configuration File

Let's create a config.ts file inside our config binder and call the form AppConfig. This is where nosotros tin set all the values we volition apply in different places in our code; for example, the URL of the API. Note that the form implements a get property which receives, as a parameter, a key/value structure and a simple method to get access to the aforementioned value. This manner, information technology will be easy to go the values just calling this.config.setting['PathAPI'] from the classes that inherit from it.

          import { Injectable } from '@athwart/cadre'; @Injectable() export class AppConfig {     private _config: { [cardinal: string]: string };     constructor() {         this._config = {              PathAPI: 'http://localhost:50498/api/'         };     }     become setting():{ [key: string]: string } {         return this._config;     }     get(cardinal: any) {         return this._config[key];     } };                  

Angular Material

Before starting the layout, let's fix the UI component framework. Of course, you lot can employ others like Bootstrap, but if yous similar the styling of Material, I practise recommend it because information technology's as well supported by Google.

To install it, we just need to run the next three commands, which we tin execute on the Visual Studio Code terminal:

          npm install --save @angular/cloth @angular/cdk npm install --save @athwart/animations npm install --save hammerjs                  

The second command is considering some Material components depend on Angular Animations. I besides recommend reading the official page to sympathise which browsers are supported and what a polyfill is.

The third command is because some Material components rely on HammerJS for gestures.

Now we tin can keep to import the component modules nosotros desire to use in our app.module.ts file:

          import {MatButtonModule, MatCheckboxModule} from '@angular/material'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/cloth/form-field'; import {MatSidenavModule} from '@angular/material/sidenav'; // ... @NgModule({   imports: [     BrowserModule,     BrowserAnimationsModule,     MatButtonModule,      MatCheckboxModule,     MatInputModule,     MatFormFieldModule,     MatSidenavModule,     AppRoutingModule,     HttpClientModule   ],                  

Side by side step is to alter the fashion.css file, adding the kind of theme you want to use:

          @import "~@angular/fabric/prebuilt-themes/deeppurple-amber.css";                  

At present import HammerJS by adding this line in the main.ts file:

          import 'hammerjs';                  

And finally all we're missing is to add the Material icons to alphabetize.html, within the head section:

          <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">                  

The Layout

In this example, we will create a elementary layout like this:

Layout example

The idea is to open/hide the menu by clicking on some button on the header. Angular Responsive volition exercise the rest of the work for us. To do this nosotros will create a layout binder and put inside it the app.component files created by default. But we will also create the aforementioned files for each section of the layout like you tin encounter in the side by side prototype. Then, app.component will be the body, head.component the header, and left-panel.component the menu.

Highlighted config folder

Now let'southward modify app.component.html as follows:

          <div *ngIf="authentication">   <app-head></app-head>   <push type="button" mat-push button (click)="drawer.toggle()">     Bill of fare   </button>   <mat-drawer-container class="instance-container" autosize>     <mat-drawer #drawer class="example-sidenav" fashion="side">       <app-left-console></app-left-panel>     </mat-drawer>     <div>       <router-outlet></router-outlet>     </div>   </mat-drawer-container> </div> <div *ngIf="!authentication"><app-login></app-login></div>                  

Basically nosotros will have an authentication property in the component which will allow us to remove the header and the menu if the user is non logged in, and instead, show a uncomplicated login page.

The caput.component.html looks like this:

          <h1>{{title}}</h1> <button mat-push button [routerLink]=" ['./logout'] ">Logout!</push button>                  

Only a button to log the user out—we volition come up back to this again afterwards. As for left-console.component.html, for now simply alter the HTML to:

          <nav>     <a routerLink="/dashboard">Dashboard</a>     <a routerLink="/users">Users</a>   </nav>                  

We've kept it simple: So far information technology's just 2 links to navigate through two different pages. (We will besides return to this afterwards.)

Now, this is what the head and the left-side component TypeScript files wait similar:

          import { Component } from '@angular/core'; @Component({   selector: 'app-head',   templateUrl: './head.component.html',   styleUrls: ['./caput.component.css'] }) export class HeadComponent {   title = 'Angular 5 Seed'; }                  
          import { Component } from '@angular/core'; @Component({   selector: 'app-left-panel',   templateUrl: './left-panel.component.html',   styleUrls: ['./left-console.component.css'] }) export class LeftPanelComponent {   championship = 'Angular 5 Seed'; }                  

Merely what nigh the TypeScript code for app.component? We volition leave a little mystery hither and break it for a while, and come back to this after implementing hallmark.

Routing

Okay, now we have Angular Material helping united states of america with the UI and a simple layout to beginning building our pages. But how can we navigate betwixt pages?

In order to create a simple instance, allow'southward create 2 pages: "User," where we can become a list of the existing users in the database, and "Dashboard," a page where we tin show some statistics.

Inside the app binder we will create a file chosen app-routing.modules.ts looking like this:

          import { NgModule }             from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuard }                from './helpers/canActivateAuthGuard'; import { LoginComponent }   from './components/login/login.component'; import { LogoutComponent }   from './components/login/logout.component'; import { DashboardComponent }   from './components/dashboard/dashboard.component'; import { UsersComponent }      from './components/users/users.component'; const routes: Routes = [   { path: '', redirectTo: '/dashboard', pathMatch: 'full', canActivate: [AuthGuard] },   { path: 'login', component: LoginComponent},   { path: 'logout', component: LogoutComponent},   { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },   { path: 'users', component: UsersComponent,canActivate: [AuthGuard] } ]; @NgModule({   imports: [ RouterModule.forRoot(routes) ],   exports: [ RouterModule ] }) export grade AppRoutingModule {}                  

It's that simple: Just importing RouterModule and Routes from @athwart/router, we can map the paths we desire to implement. Here we are creating four paths:

  • /dashboard: Our habitation page
  • /login: The page where the user can authenticate
  • /logout: A simple path to log the user out
  • /users: Our first folio where we want to listing the users from the dorsum end

Note that dashboard is our page by default, so if the user types the URL /, the page volition redirect automatically to this page. Also, take a await at the canActivate parameter: Hither we are creating a reference to the class AuthGuard, which will allow us to check if the user is logged in. If not, it redirects to the login page. In the next section, I will evidence you how to create this class.

Now, all we need to do is create the menu. Remember in the layout department when we created the left-panel.component.html file to expect like this?

          <nav>     <a routerLink="/dashboard">Dashboard</a>     <a routerLink="/users">Users</a>   </nav>                  

Here is where our code meets reality. Now nosotros can build the lawmaking and test it in the URL: You should be able to navigate from the Dashboard page to Users, but what happens if you type the URL our.site.url/users in the browser direct?

image alt text

Note that this error also appears if y'all refresh the browser after already successfully navigating to that URL via the app's side panel. To understand this error, allow me to refer to the official docs where it is really clear:

A routed awarding should support deep links. A deep link is a URL that specifies a path to a component inside the app. For example, http://www.mysite.com/users/42 is a deep link to the hero detail page that displays the hero with id: 42.

There is no consequence when the user navigates to that URL from within a running client. The Angular router interprets the URL and routes to that page and hero.

Just clicking a link in an email, entering it in the browser accost bar, or merely refreshing the browser while on the hero detail page — all of these deportment are handled by the browser itself, exterior the running application. The browser makes a direct asking to the server for that URL, bypassing the router.

A static server routinely returns index.html when information technology receives a asking for http://world wide web.mysite.com/. Only it rejects http://world wide web.mysite.com/users/42 and returns a 404 - Not Constitute error unless it is configured to return index.html instead.

To fix this event is very simple, we just need to create the service provider file configuration. Since I'm working with IIS hither, I will show you how to do it in this environment, but the concept is similar for Apache or any other web server.

So nosotros create a file inside inside the src folder called web.config that looks similar this:

          <?xml version="1.0"?> <configuration>   <organisation.webServer>   <rewrite>     <rules>       <rule name="Athwart Routes" stopProcessing="truthful">         <lucifer url=".*" />         <weather logicalGrouping="MatchAll">           <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />           <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="truthful" />         </conditions>         <activity type="Rewrite" url="/index.html" />       </rule>     </rules>   </rewrite> </system.webServer>   <organization.web>     <compilation debug="truthful"/>   </organization.web> </configuration>                  

Then nosotros need to be certain that this asset will be copied to the deployed folder. All we need to practice is modify our Angular CLI settings file angular-cli.json:

          {   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",   "project": {     "proper noun": "angular5-app"   },   "apps": [     {       "root": "src",       "outDir": "dist",       "avails": [         "assets",         "favicon.ico",         "web.config" // or whatever equivalent is required by your spider web server       ],       "alphabetize": "index.html",       "main": "principal.ts",       "polyfills": "polyfills.ts",       "test": "examination.ts",       "tsconfig": "tsconfig.app.json",       "testTsconfig": "tsconfig.spec.json",       "prefix": "app",       "styles": [         "styles.css"       ],       "scripts": [],       "environmentSource": "environments/environment.ts",       "environments": {         "dev": "environments/environment.ts",         "prod": "environments/environment.prod.ts"       }     }   ],   "e2e": {     "protractor": {       "config": "./protractor.conf.js"     }   },   "lint": [     {       "project": "src/tsconfig.app.json",       "exclude": "**/node_modules/**"     },     {       "projection": "src/tsconfig.spec.json",       "exclude": "**/node_modules/**"     },     {       "projection": "e2e/tsconfig.e2e.json",       "exclude": "**/node_modules/**"     }   ],   "exam": {     "karma": {       "config": "./karma.conf.js"     }   },   "defaults": {     "styleExt": "css",     "component": {}   } }                  

Hallmark

Do y'all remember how we had the class AuthGuard implemented to set the routing configuration? Every time we navigate to a unlike page we volition use this form to verify if the user is authenticated with a token. If not, we'll redirect automatically to the login page. The file for this is canActivateAuthGuard.ts—create it within the helpers folder and have it look like this:

          import { CanActivate, Router } from '@angular/router'; import { Injectable } from '@angular/cadre'; import { Appreciable } from 'rxjs/Observable'; import { Helpers } from './helpers'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@athwart/router'; @Injectable() export class AuthGuard implements CanActivate {   constructor(individual router: Router, private helper: Helpers) {}   canActivate(road: ActivatedRouteSnapshot, state: RouterStateSnapshot): Appreciable<boolean> | boolean {     if (!this.helper.isAuthenticated()) {       this.router.navigate(['/login']);       return false;     }     render true;   } }                  

So every time we change the page the method canActivate will be called, which will check if the user is authenticated, and if not, we use our Router instance to redirect to the login page. Just what is this new method on the Helper course? Nether the helpers folder permit's create a file helpers.ts. Here nosotros demand to manage localStorage, where nosotros volition store the token we become from the back finish.

Annotation

Regarding localStorage, you can also utilise cookies or sessionStorage, and the decision volition depend on the behavior we want to implement. Equally the proper name suggests, sessionStorage is only available for the elapsing of the browser session, and is deleted when the tab or window is closed; it does, still, survive page reloads. If the data you are storing needs to be available on an ongoing footing, so localStorage is preferable to sessionStorage. Cookies are primarily for reading server-side, whereas localStorage tin just be read client-side. And so the question is, in your app, who needs this data---the client or the server?

          import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Subject } from 'rxjs/Subject'; @Injectable() export class Helpers  {     private authenticationChanged = new Subject area<boolean>();     constructor() {     }     public isAuthenticated():boolean {         return (!(window.localStorage['token'] === undefined ||              window.localStorage['token'] === null ||             window.localStorage['token'] === 'aught' ||             window.localStorage['token'] === 'undefined' ||             window.localStorage['token'] === ''));     }     public isAuthenticationChanged():whatsoever {         return this.authenticationChanged.asObservable();     }     public getToken():any {         if( window.localStorage['token'] === undefined ||              window.localStorage['token'] === aught ||             window.localStorage['token'] === 'null' ||             window.localStorage['token'] === 'undefined' ||             window.localStorage['token'] === '') {             render '';         }         let obj = JSON.parse(window.localStorage['token']);         return obj.token;     }     public setToken(data:any):void {         this.setStorageToken(JSON.stringify(data));     }     public failToken():void {         this.setStorageToken(undefined);     }     public logout():void {         this.setStorageToken(undefined);     }     private setStorageToken(value: whatsoever):void {         window.localStorage['token'] = value;         this.authenticationChanged.next(this.isAuthenticated());     } }                  

Is our authentication code making sense now? We'll come back to the Subject grade later, but right at present permit's circumvolve back for a minute to the routing configuration. Have a look at this line:

                      { path: 'logout', component: LogoutComponent},                  

This is our component to log out of the site, and information technology'southward just a simple grade to clean out the localStorage. Let's create information technology under the components/login folder with the name of logout.component.ts:

          import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Helpers } from '../../helpers/helpers'; @Component({   selector: 'app-logout',   template:'<ng-content></ng-content>'  }) consign grade LogoutComponent implements OnInit {   constructor(private router: Router, individual helpers: Helpers) { }   ngOnInit() {     this.helpers.logout();     this.router.navigate(['/login']);   } }                  

So every time we go to the URL /logout, the localStorage will be removed and the site will redirect to the login page. Finally, permit's create login.component.ts like this:

          import { Component, OnInit } from '@angular/cadre'; import { Router } from '@angular/router'; import { TokenService } from '../../services/token.service'; import { Helpers } from '../../helpers/helpers'; @Component({   selector: 'app-login',   templateUrl: './login.component.html',   styleUrls: [ './login.component.css' ] }) export form LoginComponent implements OnInit {   constructor(private helpers: Helpers, individual router: Router, private tokenService: TokenService) { }   ngOnInit() {   }   login(): void {     let authValues = {"Username":"pablo", "Password":"secret"};     this.tokenService.auth(authValues).subscribe(token => {       this.helpers.setToken(token);       this.router.navigate(['/dashboard']);     });   } }                  

As you can see, for the moment we've difficult-coded our credentials here. Note that here we are calling a service form; we will create these services classes to get admission to our back end in the adjacent section.

Finally, we need to go back to the app.component.ts file, the layout of the site. Here, if the user is authenticated, information technology volition prove the menu and header sections, but if not, the layout will change to evidence just our login folio.

          export class AppComponent implements AfterViewInit {   subscription: Subscription;   authentication: boolean;   constructor(private helpers: Helpers) {   }   ngAfterViewInit() {     this.subscription = this.helpers.isAuthenticationChanged().piping(       startWith(this.helpers.isAuthenticated()),       delay(0)).subscribe((value) =>         this.authentication = value       );   }   title = 'Angular v Seed';   ngOnDestroy() {     this.subscription.unsubscribe();   } }                  

Remember the Subject class in our helper course? This is an Appreciable. Appreciables provide back up for passing letters between publishers and subscribers in your awarding. Every time the hallmark token changes, the hallmark holding will be updated. Reviewing the app.component.html file, it volition probably make more sense now:

          <div *ngIf="authentication">   <app-head></app-head>   <push button blazon="push" mat-button (click)="drawer.toggle()">     Menu   </button>   <mat-drawer-container class="case-container" autosize>     <mat-drawer #drawer class="example-sidenav" way="side">       <app-left-panel></app-left-panel>     </mat-drawer>     <div>       <router-outlet></router-outlet>     </div>   </mat-drawer-container> </div> <div *ngIf="!authentication"><app-login></app-login></div>                  

Services

At this signal nosotros are navigating to different pages, authenticating our client side, and rendering a very simple layout. But how we tin become data from the dorsum terminate? I strongly recommend doing all dorsum-end admission from service classes in particular. Our first service will be inside the services folder, chosen token.service.ts:

          import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { catchError, map, tap } from 'rxjs/operators'; import { AppConfig } from '../config/config'; import { BaseService } from './base.service'; import { Token } from '../models/token'; import { Helpers } from '../helpers/helpers'; @Injectable() export course TokenService extends BaseService {   individual pathAPI = this.config.setting['PathAPI'];   public errorMessage: string;   constructor(private http: HttpClient, private config: AppConfig, helper: Helpers) { super(helper); }   auth(data: whatever): any {     let body = JSON.stringify(data);     return this.getToken(body);   }   private getToken (body: any): Observable<any> {     render this.http.post<any>(this.pathAPI + 'token', body, super.header()).pipe(         catchError(super.handleError)       );   } }                  

The first call to the back end is a Mail telephone call to the token API. The token API does not need the token string in the header, but what happen if nosotros call another endpoint? As you can see here, TokenService (and service classes in full general) inherit from the BaseService course. Let's take a look at this:

          import { Injectable } from '@athwart/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Appreciable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { catchError, map, tap } from 'rxjs/operators'; import { Helpers } from '../helpers/helpers'; @Injectable() export grade BaseService {     constructor(private helper: Helpers) { }     public extractData(res: Response) {         allow body = res.json();         return torso || {};       }       public handleError(error: Response | any) {         // In a existent-earth app, we might use a remote logging infrastructure         let errMsg: string;         if (fault instanceof Response) {           const body = mistake.json() || '';           const err = body || JSON.stringify(trunk);           errMsg = `${error.status} - ${error.statusText || ''} ${err}`;         } else {           errMsg = error.message ? error.message : error.toString();         }         console.mistake(errMsg);         return Observable.throw(errMsg);     }       public header() {         let header = new HttpHeaders({ 'Content-Type': 'application/json' });         if(this.helper.isAuthenticated()) {           header = header.suspend('Authorization', 'Bearer ' + this.helper.getToken());          }         return { headers: header };       }       public setToken(data:any) {         this.helper.setToken(data);       }       public failToken(mistake: Response | any) {         this.helper.failToken();         return this.handleError(Response);       }  }                  

So every time we make an HTTP call, we implement the header of the request just using super.header. If the token is in localStorage then it will be appended inside the header, just if not, we will just set the JSON format. Another thing we can see here is what happens if authentication fails.

The login component will phone call the service class and the service class volition telephone call the back end. One time we take the token, the helper class volition manage the token, and now nosotros are ready to go the listing of users from our database.

To go data from the database, first be sure we match the model classes with the back-finish view models in our response.

In user.ts:

          consign course User {   id: number;   proper noun: string; }                  

And we can create now the user.service.ts file:

          import { Injectable } from '@angular/cadre'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { catchError, map, tap } from 'rxjs/operators'; import { BaseService } from './base.service'; import { User } from '../models/user'; import { AppConfig } from '../config/config'; import { Helpers } from '../helpers/helpers'; @Injectable() consign grade UserService extends BaseService {   private pathAPI = this.config.setting['PathAPI'];   constructor(private http: HttpClient, private config: AppConfig, helper: Helpers) { super(helper); }   /** Become heroes from the server */   getUsers (): Observable<User[]> {     return this.http.get(this.pathAPI + 'user', super.header()).pipe(     catchError(super.handleError));   }                  

The Back Terminate

Quick First

Welcome to the first step of our Web API Core 2 awarding. The get-go thing we demand is to create an ASP.Internet Core Web Application, which we volition telephone call SeedAPI.Web.API.

Creating a new file

Exist certain to choose the Empty template for a make clean showtime like you can come across beneath:

choose the Empty template

That's all, we create the solution starting with an empty spider web application. Now our compages volition be equally nosotros list below so volition have to create the unlike projects:

our current architecture

To practice this, for each one just right-click the Solution and add a "Course Library (.Cyberspace Core)" projection.

add a "Class Library (.NET Core)"

The Architecture

In the previous section we created eight projects, but what are they for? Here is a simple description of each 1:

  • Spider web.API: This is our startup projection and where endpoints are created. Here we volition fix upwardly JWT, injection dependencies, and controllers.
  • ViewModels: Here we perform conversions from the blazon of data that controllers will return in the responses to the front terminate. Information technology is a adept practise to match these classes with the front-end models.
  • Interfaces: This volition be helpful in implementing injection dependencies. The compelling benefit of a statically typed language is that the compiler can aid verify that a contract which your code relies upon is actually met.
  • Eatables: All the shared behaviors and utility code volition be hither.
  • Models: It is a good practise not to match the database directly with the front-end-facing ViewModels, so the purpose of Models is to create entity database classes contained of the forepart end. That will let united states in futurity to change our database without necessarily having an bear on on our forepart. It also helps when we only want to practise some refactoring.
  • Maps: Here is where we map ViewModels to Models and vice-versa. This stride is chosen betwixt controllers and Services.
  • Services: A library to shop all the business logic.
  • Repositories: This is the but place where we telephone call the database.

The references volition look like this:

Diagram of references

JWT-based Authentication

In this department, we will see the bones configuration of token hallmark and go a bit deeper on the subject of security.

To start setting the JSON web token (JWT) permit's create the next class inside the App_Start folder called JwtTokenConfig.cs. The code inside will look like this:

          namespace SeedAPI.Web.API.App_Start {     public form JwtTokenConfig     {         public static void AddAuthentication(IServiceCollection services, IConfiguration configuration)         {             services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)             .AddJwtBearer(options =>             {                 options.TokenValidationParameters = new TokenValidationParameters                 {                     ValidateIssuer = true,                     ValidateAudience = truthful,                     ValidateLifetime = true,                     ValidateIssuerSigningKey = true,                     ValidIssuer = configuration["Jwt:Issuer"],                     ValidAudience = configuration["Jwt:Issuer"],                     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]))                 };                 services.AddCors();             });         }     } }                  

The values of the validation parameters volition depend on the requirement of each project. The valid user and audience we tin can set reading the configuration file appsettings.json:

          "Jwt": {     "Cardinal": "veryVerySecretKey",     "Issuer": "http://localhost:50498/"   }                  

And then nosotros need merely phone call it from the ConfigureServices method in startup.cs:

                      // This method gets chosen by the runtime. Use this method to add services to the container.         public void ConfigureServices(IServiceCollection services)         {             DependencyInjectionConfig.AddScope(services);             JwtTokenConfig.AddAuthentication(services, Configuration);             DBContextConfig.Initialize(services, Configuration);             services.AddMvc();         }                  

Now we are ready to create our first controller called TokenController.cs. The value we set in appsettings.json to "veryVerySecretKey" should friction match the one we use to create the token, but first, let's create the LoginViewModel inside our ViewModels project:

          namespace SeedAPI.ViewModels {     public course LoginViewModel : IBaseViewModel     {         public cord username { become; set; }         public string countersign { go; fix; }     } }                  

And finally the controller:

          namespace SeedAPI.Web.API.Controllers {     [Route("api/Token")]     public course TokenController : Controller     {         private IConfiguration _config;         public TokenController(IConfiguration config)         {             _config = config;         }         [AllowAnonymous]         [HttpPost]         public dynamic Post([FromBody]LoginViewModel login)         {             IActionResult response = Unauthorized();             var user = Authenticate(login);             if (user != naught)             {                 var tokenString = BuildToken(user);                 response = Ok(new { token = tokenString });             }             return response;         }         private string BuildToken(UserViewModel user)         {             var central = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Cardinal"]));             var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);             var token = new JwtSecurityToken(_config["Jwt:Issuer"],               _config["Jwt:Issuer"],               expires: DateTime.Now.AddMinutes(30),               signingCredentials: creds);             return new JwtSecurityTokenHandler().WriteToken(token);         }         individual UserViewModel Cosign(LoginViewModel login)         {             UserViewModel user = null;             if (login.username == "pablo" && login.password == "secret")             {                 user = new UserViewModel { proper name = "Pablo" };             }             return user;         }     } }                  

The BuildToken method will create the token with the given security code. The Authenticate method simply has user validation difficult-coded for the moment, but we volition need to call the database to validate information technology in the stop.

The Application Context

Setting up Entity Framework is really like shooting fish in a barrel since Microsoft launched the Core 2.0 version—EF Core 2 for short. We are going to get into depth with a lawmaking-first model using identityDbContext, and then commencement be certain yous have installed all the dependencies. You can utilize NuGet to manage it:

Getting dependencies

Using the Models project we tin create hither inside the Context folder two files, ApplicationContext.cs and IApplicationContext.cs. Besides, nosotros volition demand an EntityBase class.

Classes

The EntityBase files volition be inherited by each entity model, but User.cs is an identity grade and the simply entity that volition inherit from IdentityUser. Beneath are both classes:

          namespace SeedAPI.Models {     public class User : IdentityUser     {         public cord Proper noun { get; set; }     } }                  
          namespace SeedAPI.Models.EntityBase {     public class EntityBase     {         public DateTime? Created { go; set; }         public DateTime? Updated { get; prepare; }         public bool Deleted { become; ready; }         public EntityBase()         {             Deleted = false;         }         public virtual int IdentityID()         {             return 0;         }         public virtual object[] IdentityID(bool dummy = true)         {             return new List<object>().ToArray();         }     } }                  

At present we are fix to create ApplicationContext.cs, which will wait similar this:

          namespace SeedAPI.Models.Context {     public class ApplicationContext : IdentityDbContext<User>, IApplicationContext     {         private IDbContextTransaction dbContextTransaction;         public ApplicationContext(DbContextOptions options)             : base(options)         {                    }         public DbSet<User> UsersDB { go; ready; }         public new void SaveChanges()         {             base.SaveChanges();         }         public new DbSet<T> Set<T>() where T : class         {             return base.Ready<T>();         }         public void BeginTransaction()         {             dbContextTransaction = Database.BeginTransaction();         }         public void CommitTransaction()         {             if (dbContextTransaction != aught)             {                 dbContextTransaction.Commit();             }         }         public void RollbackTransaction()         {             if (dbContextTransaction != goose egg)             {                 dbContextTransaction.Rollback();             }         }         public void DisposeTransaction()         {             if (dbContextTransaction != nil)             {                 dbContextTransaction.Dispose();             }         }     } }                  

We are actually close, but start we, will demand to create more classes, this fourth dimension in the App_Start folder located in the Web.API projection. The start class is to initialize the application context and the 2d one is to create sample data just for the purpose of testing during evolution.

          namespace SeedAPI.Web.API.App_Start {     public grade DBContextConfig     {         public static void Initialize(IConfiguration configuration, IHostingEnvironment env, IServiceProvider svp)         {             var optionsBuilder = new DbContextOptionsBuilder();             if (env.IsDevelopment()) optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));             else if (env.IsStaging()) optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));             else if (env.IsProduction()) optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));             var context = new ApplicationContext(optionsBuilder.Options);             if(context.Database.EnsureCreated())             {                 IUserMap service = svp.GetService(typeof(IUserMap)) as IUserMap;                 new DBInitializeConfig(service).DataTest();             }         }         public static void Initialize(IServiceCollection services, IConfiguration configuration)         {             services.AddDbContext<ApplicationContext>(options =>               options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));         }     } }                  
          namespace SeedAPI.Spider web.API.App_Start {     public course DBInitializeConfig     {         private IUserMap userMap;         public DBInitializeConfig (IUserMap _userMap)         {             userMap = _userMap;         }         public void DataTest()         {             Users();         }         private void Users()         {             userMap.Create(new UserViewModel() { id = 1, name = "Pablo" });             userMap.Create(new UserViewModel() { id = two, name = "Diego" });         }     } }                  

And nosotros call them from our startup file:

                      // This method gets chosen by the runtime. Utilise this method to add services to the container.         public void ConfigureServices(IServiceCollection services)         {             DependencyInjectionConfig.AddScope(services);             JwtTokenConfig.AddAuthentication(services, Configuration);             DBContextConfig.Initialize(services, Configuration);             services.AddMvc();         } // ...  // This method gets called by the runtime. Use this method to configure the HTTP asking pipeline.         public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider svp)         {             if (env.IsDevelopment())             {                 app.UseDeveloperExceptionPage();             }             DBContextConfig.Initialize(Configuration, env, svp);             app.UseCors(builder => builder                 .AllowAnyOrigin()                 .AllowAnyMethod()                 .AllowAnyHeader()                 .AllowCredentials());             app.UseAuthentication();             app.UseMvc();         }                  

Dependency Injection

It is a practiced exercise to apply dependency injection to move among different projects. This will assist us to communicate between controllers and mappers, mappers and services, and services and repositories.

Inside the folder App_Start we volition create the file DependencyInjectionConfig.cs and it volition look like this:

          namespace SeedAPI.Web.API.App_Start {     public class DependencyInjectionConfig     {         public static void AddScope(IServiceCollection services)         {             services.AddScoped<IApplicationContext, ApplicationContext>();             services.AddScoped<IUserMap, UserMap>();             services.AddScoped<IUserService, UserService>();             services.AddScoped<IUserRepository, UserRepository>();         }     } }                  
image alt text

Nosotros volition demand to create for each new entity a new Map, Service, and Repository, and match them to this file. Then we just need to call it from the startup.cs file:

          // This method gets called past the runtime. Utilize this method to add together services to the container.         public void ConfigureServices(IServiceCollection services)         {             DependencyInjectionConfig.AddScope(services);             JwtTokenConfig.AddAuthentication(services, Configuration);             DBContextConfig.Initialize(services, Configuration);             services.AddMvc();         }                  

Finally, when we demand to get the users list from the database, we can create a controller using this dependency injection:

          namespace SeedAPI.Web.API.Controllers {     [Route("api/[controller]")]     [Authorize]     public course UserController : Controller     {         IUserMap userMap;         public UserController(IUserMap map)         {             userMap = map;         }         // GET api/user         [HttpGet]         public IEnumerable<UserViewModel> Become()         {             return userMap.GetAll(); ;         }         // GET api/user/5         [HttpGet("{id}")]         public string Get(int id)         {             return "value";         }         // POST api/user         [HttpPost]         public void Post([FromBody]cord user)         {         }         // PUT api/user/5         [HttpPut("{id}")]         public void Put(int id, [FromBody]string user)         {         }         // DELETE api/user/5         [HttpDelete("{id}")]         public void Delete(int id)         {         }     } }                  

Look how the Authorize attribute is present hither to be sure that the forepart end has logged in and how dependency injection works in the constructor of the grade.

We finally have a call to the database just first, we need to sympathise the Map project.

The Maps Projection

This footstep is just to map ViewModels to and from database models. We must create one for each entity, and, following our previous, example the UserMap.cs file will look like this:

          namespace SeedAPI.Maps {     public form UserMap : IUserMap     {         IUserService userService;         public UserMap(IUserService service)         {             userService = service;         }         public UserViewModel Create(UserViewModel viewModel)         {             User user = ViewModelToDomain(viewModel);             render DomainToViewModel(userService.Create(user));         }         public bool Update(UserViewModel viewModel)         {             User user = ViewModelToDomain(viewModel);             return userService.Update(user);         }         public bool Delete(int id)         {             render userService.Delete(id);         }         public List<UserViewModel> GetAll()         {             return DomainToViewModel(userService.GetAll());         }         public UserViewModel DomainToViewModel(User domain)         {             UserViewModel model = new UserViewModel();             model.name = domain.Name;             return model;         }         public List<UserViewModel> DomainToViewModel(List<User> domain)         {             Listing<UserViewModel> model = new List<UserViewModel>();             foreach (User of in domain)             {                 model.Add(DomainToViewModel(of));             }             render model;         }         public User ViewModelToDomain(UserViewModel officeViewModel)         {             User domain = new User();             domain.Proper noun = officeViewModel.proper name;             return domain;         }     } }                  

Looks like once more, dependency injection is working in the constructor of the form, linking Maps to the Services project.

The Services Project

In that location is not too much to say hither: Our instance is actually simple and we don't have business logic or lawmaking to write here. This projection would testify useful in future advanced requirements when we need to calculate or do some logic before or later the database or controller steps. Following the case the grade will look pretty bare:

          namespace SeedAPI.Services {     public class UserService : IUserService     {         private IUserRepository repository;         public UserService(IUserRepository userRepository)         {             repository = userRepository;         }         public User Create(User domain)         {             return repository.Relieve(domain);         }         public bool Update(User domain)         {             return repository.Update(domain);         }         public bool Delete(int id)         {             return repository.Delete(id);         }         public List<User> GetAll()         {             return repository.GetAll();         }     } }                  

The Repositories Project

We're getting to the last section of this tutorial: We just need to make calls to the database, so we create a UserRepository.cs file where we can read, insert, or update users in the database.

          namespace SeedAPI.Repositories {     public class UserRepository : BaseRepository, IUserRepository     {         public UserRepository(IApplicationContext context)             : base of operations(context)         { }         public User Save(User domain)         {             try             {                 var the states = InsertUser<User>(domain);                 render the states;             }             grab (Exception ex)             {                 //ErrorManager.ErrorHandler.HandleError(ex);                 throw ex;             }         }         public bool Update(User domain)         {             try             {                 //domain.Updated = DateTime.Now;                 UpdateUser<User>(domain);                 return truthful;             }             take hold of (Exception ex)             {                 //ErrorManager.ErrorHandler.HandleError(ex);                 throw ex;             }         }         public bool Delete(int id)         {             attempt             {                 User user = Context.UsersDB.Where(ten => ten.Id.Equals(id)).FirstOrDefault();                 if (user != null)                 {                     //Delete<User>(user);                     return truthful;                 }                 else                 {                     render false;                 }             }             catch (Exception ex)             {                 //ErrorManager.ErrorHandler.HandleError(ex);                 throw ex;             }         }         public List<User> GetAll()         {             try             {                 return Context.UsersDB.OrderBy(x => ten.Proper name).ToList();             }             take hold of (Exception ex)             {                 //ErrorManager.ErrorHandler.HandleError(ex);                 throw ex;             }         }     } }                  

Summary

In this article, I explained how to create a good architecture using Angular five and Web API Core 2. At this bespeak, you've created the base for a big project with code that supports a large growth in requirements.

The truth is, cipher competes with JavaScript in the front end end and what can compete with C# if yous need the support of SQL Server and Entity Framework in the back end? Then the idea of this article was to combine the best of two worlds and I promise you've enjoyed it.

What's Next?

If you are working in a squad of Angular developers probably in that location could be different developers working in the forepart and the back end, then a good thought to synchronize the efforts of both teams could exist integrating Swagger with Web API 2. Swagger is a neat tool to certificate and exam your RESTFul APIs. Read the Microsoft guide: Get started with Swashbuckle and ASP.NET Core.

If you're all the same very new to Angular 5 and are having trouble following along, read An Angular five Tutorial: Step past Step Guide to Your Offset Angular 5 App by fellow Toptaler Sergey Moiseev.

kingmarpoe.blogspot.com

Source: https://www.toptal.com/angular/angular-5-asp-net-core

0 Response to "Asp.net Core 2 Microsoft Account Is Experiencing Technical Problems. Please Try Again Later."

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel