Olen ajatellut blogikirjoituksen kirjoittamista siitä lähtien, kun Angularin ensimmäinen versio käytännössä tappoi Microsoftin asiakaspuolella. Teknologiat, kuten ASP.Net, Web Forms ja MVC Razor, ovat vanhentuneet ja korvattu JavaScript-kehyksellä, joka ei ole aivan Microsoft. Kuitenkin Angularin toisen version jälkeen Microsoft ja Google ovat työskennelleet yhdessä Angular 2: n luomiseksi, ja juuri silloin kaksi suosikkitekniikkani alkoivat toimia yhdessä.
Haluan tässä blogissa auttaa ihmisiä luomaan parhaan arkkitehtuurin, joka yhdistää nämä kaksi maailmaa. Oletko valmis? Nyt sitä mennään!
Rakennat Angular 5 -asiakasohjelman, joka käyttää RESTful Web API Core 2 -palvelua.
Asiakaspuoli:
Palvelinpuoli:
Huomautus
Tässä blogiviestissä oletetaan, että lukijalla on jo perustiedot TypeScriptistä, Angular-moduuleista, komponenteista ja tuonnista / viennistä. Tämän viestin tavoitteena on luoda hyvä arkkitehtuuri, jonka avulla koodi kasvaa ajan myötä. |
Aloitetaan valitsemalla IDE. Tietenkin tämä on vain minun mieltymykseni, ja voit käyttää sitä, jonka kanssa tunnet olosi mukavammaksi. Minun tapauksessani käytän Visual Studio -koodia ja Visual Studio 2017: tä.
Miksi kaksi erilaista IDE: tä? Koska Microsoft loi Visual Studio -koodin käyttöliittymälle, en voi lopettaa tämän IDE: n käyttöä. Joka tapauksessa näemme myös, kuinka Angular 5 integroidaan ratkaisuprojektin sisälle, mikä auttaa sinua, jos olet sellainen kehittäjä, joka mieluummin etsii sekä takapään että etupuolen yhdellä F5: llä.
Tietoja käyttöliittymästä voit asentaa uusimman Visual Studio 2017 -version, jolla on ilmainen kehittäjille tarkoitettu versio, mutta joka on erittäin kattava: Yhteisö.
Joten tässä on luettelo asioista, jotka meidän on asennettava tähän opetusohjelmaan:
Huomautus
Varmista, että sinulla on vähintään solmu 6.9.x ja npm 3.x.x suorittamalla node -v ja npm -v pääte- tai konsoli-ikkunassa. Vanhemmat versiot tuottavat virheitä, mutta uudemmat versiot ovat hyviä. |
Hauskuus alkakoon! Ensimmäinen asia, joka meidän on tehtävä, on asentaa Angular CLI maailmanlaajuisesti, joten avaa node.js-komentokehote ja suorita tämä komento:
npm install -g @angular/cli
Okei, nyt meillä on moduulipakettimme. Tämä asentaa moduulin yleensä käyttäjäkansioon. Aliaksen ei pitäisi olla oletusarvoisesti tarpeellinen, mutta jos tarvitset sitä, voit suorittaa seuraavan rivin:
alias ng='/.npm/lib/node_modules/angular-cli/bin/ng'
Seuraava vaihe on luoda uusi projekti. Minä kutsun sitä angular5-app
. Ensin siirrymme kansioon, johon haluamme luoda sivuston, ja sitten:
ng new angular5-app
Vaikka voit testata uutta verkkosivustoasi juuri käynnissä ng serve --open
, suosittelen testaamaan sivuston suosikkiverkkopalvelustasi. Miksi? Jotkut ongelmat voivat ilmetä vain tuotannossa ja sivuston rakentamisessa ng build
on lähin tapa lähestyä tätä ympäristöä. Sitten voimme avata kansion angular5-app
Visual Studio -koodilla ja suorita ng build
päätelaitteessa:
Uusi kansio nimeltä dist
luodaan ja voimme palvella sitä IIS: llä tai haluamallasi web-palvelimella. Sitten voit kirjoittaa URL-osoitteen selaimeen ja… valmis!
Huomautus
Tämän opetusohjelman tarkoituksena ei ole näyttää verkkopalvelimen perustamista, joten oletan, että sinulla on jo tämä tieto. |
src
Kansio
Oma src
kansion rakenne on seuraava: app
kansio meillä on components
missä luomme jokaiselle kulmakomponentille css
, ts
, spec
ja html
tiedostot. Luomme myös config
kansio sivuston kokoonpanon säilyttämiseksi, directives
on kaikki mukautetut direktiivimme, helpers
sisältää yhteisen koodin, kuten todennuksen hallinta, layout
sisältää pääosat, kuten rungon, pään ja sivupaneelit, models
säilyttää taustanäkymämalleihin sopivan ja lopuksi services
on koodi kaikille puheluille.
app
: N ulkopuolella kansio, säilytämme oletuksena luodut kansiot, kuten assets
ja environments
sekä juuritiedostot.
Luodaan config.ts
tiedosto config
sisällä -kansioon ja soita luokkaan AppConfig
. Tässä voimme asettaa kaikki arvot, joita käytämme koodin eri paikoissa; esimerkiksi API: n URL-osoite. Huomaa, että luokka toteuttaa a get
ominaisuus, joka vastaanottaa parametrina avaimen / arvon rakenteen ja yksinkertaisen menetelmän päästä samaan arvoon. Tällä tavalla on helppo saada arvot, jotka vain kutsuvat this.config.setting['PathAPI']
luokista, jotka perivät siitä.
import { Injectable } from '@angular/core'; @Injectable() export class AppConfig { private _config: { [key: string]: string }; constructor() { this._config = { PathAPI: 'http://localhost:50498/api/' }; } get setting():{ [key: string]: string } { return this._config; } get(key: any) { return this._config[key]; } };
Määritetään käyttöliittymän komponenttikehys ennen asettelun aloittamista. Voit tietysti käyttää muita, kuten Bootstrapia, mutta jos pidät materiaalin tyylistä, suosittelen sitä, koska Google tukee myös sitä.
Sen asentamiseksi meidän on vain suoritettava seuraavat kolme komentoa, jotka voimme suorittaa Visual Studio Code -päätteessä:
npm install --save @angular/material @angular/cdk npm install --save @angular/animations npm install --save hammerjs
Toinen komento johtuu siitä, että jotkut materiaalikomponentit riippuvat kulma-animaatioista. Suosittelen myös lukemista virallinen sivu ymmärtää, mitä selaimia tuetaan ja mikä on polyfill-täyttö.
Kolmas komento johtuu siitä, että jotkut materiaalikomponentit luottavat HammerJS: ään eleissä.
Nyt voimme jatkaa komponenttimoduulien tuomista app.module.ts
tiedosto:
import {MatButtonModule, MatCheckboxModule} from '@angular/material'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatSidenavModule} from '@angular/material/sidenav'; // ... @NgModule({ imports: [ BrowserModule, BrowserAnimationsModule, MatButtonModule, MatCheckboxModule, MatInputModule, MatFormFieldModule, MatSidenavModule, AppRoutingModule, HttpClientModule ],
Seuraava vaihe on muuttaa style.css
tiedosto, lisäämällä haluamasi teema:
@import ' [email protected] /material/prebuilt-themes/deeppurple-amber.css';
Tuo nyt HammerJS lisäämällä tämä rivi main.ts
tiedosto:
import 'hammerjs';
Ja lopulta kaikki mitä meiltä puuttuu, on lisätä materiaalikuvakkeet index.html
-pään osaan:
layout
Tässä esimerkissä luomme seuraavanlaisen yksinkertaisen asettelun:
Ajatuksena on avata / piilottaa valikko napsauttamalla jotakin painiketta otsikossa. Kulmikas reagoiva tekee lopun työn puolestamme. Tätä varten luomme app.component
kansion ja aseta sen sisälle app.component
oletuksena luodut tiedostot. Mutta luomme myös samat tiedostot asettelun jokaiselle osalle, kuten näet seuraavassa kuvassa. Sitten head.component
on ruumis, left-panel.component
otsikko ja app.component.html
ruokalista.
Muutetaan nyt Menu
seuraavasti:
authentication
Pohjimmiltaan meillä on head.component.html
komponentin ominaisuus, jonka avulla voimme poistaa otsikon ja valikon, jos käyttäjä ei ole kirjautunut sisään, ja näyttää sen sijaan yksinkertaisen kirjautumissivun.
Logout!
näyttää tältä:
left-panel.component.html
Vain painike kirjautua ulos käyttäjästä - palaamme asiaan myöhemmin uudelleen. Mitä tulee Dashboard Users
: een, muuta vain HTML: ksi:
import { Component } from '@angular/core'; @Component({ selector: 'app-head', templateUrl: './head.component.html', styleUrls: ['./head.component.css'] }) export class HeadComponent { title = 'Angular 5 Seed'; }
Olemme pitäneet sen yksinkertaisena: toistaiseksi se on vain kaksi linkkiä selaamaan kahdella eri sivulla. (Palataan myös tähän myöhemmin.)
Nyt pää- ja vasemmanpuoleiset TypeScript-tiedostot näyttävät tältä:
import { Component } from '@angular/core'; @Component({ selector: 'app-left-panel', templateUrl: './left-panel.component.html', styleUrls: ['./left-panel.component.css'] }) export class LeftPanelComponent { title = 'Angular 5 Seed'; }
app.component
Mutta entä app
-tyyppinen TypeScript-koodi? Jätämme tänne pienen mysteerin ja keskeytämme sen hetkeksi ja palaamme tähän todentamisen jälkeen.
Okei, nyt meillä on kulmamateriaali, joka auttaa meitä käyttöliittymässä ja yksinkertainen asettelu sivujemme rakentamisen aloittamiseksi. Mutta miten voimme navigoida sivujen välillä?
Yksinkertaisen esimerkin luomiseksi luodaan kaksi sivua: 'Käyttäjä', josta voimme saada luettelon tietokannan nykyisistä käyttäjistä, ja 'Hallintapaneeli' -sivu, jolla voimme näyttää joitain tilastoja.
app-routing.modules.ts
: N sisällä kansion luomme tiedoston nimeltä 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 class AppRoutingModule {}
näyttää tältä:
RouterModule
Se on niin yksinkertaista: vain tuoda Routes
ja @angular/router
alkaen /dashboard
voimme kartoittaa polut, jotka haluamme toteuttaa. Tässä luomme neljä polkua:
/login
: Kotisivumme/logout
: Sivu, jolla käyttäjä voi todentaa/users
: Yksinkertainen polku käyttäjän kirjautumiseen ulosdashboard
: Ensimmäinen sivu, johon haluamme listata käyttäjät takapäästäHuomaa, että /
on sivumme oletuksena, joten jos käyttäjä kirjoittaa URL-osoitteen canActivate
, sivu ohjaa automaattisesti tälle sivulle. Katso myös AuthGuard
parametri: Tässä luomme viitteen luokkaan left-panel.component.html
, jonka avulla voimme tarkistaa, onko käyttäjä kirjautunut sisään. Jos ei, se ohjaa sisäänkirjautumissivulle. Seuraavassa osassa näytän sinulle, kuinka luodaan tämä luokka.
Nyt meidän tarvitsee vain luoda valikko. Muista asetteluosassa, kun loimme Dashboard Users
tiedosto näyttää tältä?
our.site.url/users
Tässä koodimme kohtaa todellisuuden. Nyt voimme rakentaa koodin ja testata sen URL-osoitteessa: Sinun pitäisi pystyä siirtymään Hallintapaneeli-sivulta Käyttäjät-kohtaan, mutta mitä tapahtuu, jos kirjoitat URL-osoitteen http://www.mysite.com/users/42
suoraan selaimessa?
Huomaa, että tämä virhe näkyy myös, jos päivität selaimen sen jälkeen, kun olet jo siirtynyt kyseiseen URL-osoitteeseen sovelluksen sivupaneelin kautta. Sallikaa minun ymmärtää tämä virhe viralliset asiakirjat missä on todella selvää:
Reititetyn sovelluksen tulisi tukea syvälinkkejä. Syvä linkki on URL-osoite, joka määrittää polun sovelluksen sisäiseen komponenttiin. Esimerkiksi
http://www.mysite.com/
on syvä linkki sankarin tietosivulle, joka näyttää sankarin, jonka tunnus on: 42.Ei ole ongelma, kun käyttäjä siirtyy kyseiseen URL-osoitteeseen käynnissä olevan asiakkaan sisällä. Kulmareititin tulkitsee URL-osoitteen ja reitit tälle sivulle ja sankarille.
Mutta napsauttamalla linkkiä sähköpostissa, kirjoittamalla se selaimen osoiteriville tai vain päivittämällä selain ollessasi sankarin tietosivulla - kaikki nämä toimet hoitaa selain itse, käynnissä olevan sovelluksen ulkopuolella. Selain pyytää suoraa palvelimelta kyseistä URL-osoitetta ohittamalla reitittimen.Staattinen palvelin palauttaa rutiininomaisesti index.html-tiedoston, kun se saa pyynnön
http://www.mysite.com/users/42
Mutta se hylkääsrc
ja palauttaa 404 - Ei löydy -virheen, ellei sitä ole määritetty palauttamaan sen sijaan index.html-tiedostoa.
Tämän ongelman korjaaminen on hyvin yksinkertaista, meidän on vain luotava palveluntarjoajan tiedostokokoonpano. Koska työskentelen IIS: n kanssa täällä, näytän sinulle, miten se tehdään tässä ympäristössä, mutta konsepti on samanlainen Apachessa tai muussa verkkopalvelimessa.
Joten luomme tiedoston sisälle web.config
kansio nimeltä angular-cli.json
joka näyttää tältä:
{ '$schema': './node_modules/@angular/cli/lib/config/schema.json', 'project': { 'name': 'angular5-app' }, 'apps': [ { 'root': 'src', 'outDir': 'dist', 'assets': [ 'assets', 'favicon.ico', 'web.config' // or whatever equivalent is required by your web server ], 'index': 'index.html', 'main': 'main.ts', 'polyfills': 'polyfills.ts', 'test': 'test.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/**' }, { 'project': 'src/tsconfig.spec.json', 'exclude': '**/node_modules/**' }, { 'project': 'e2e/tsconfig.e2e.json', 'exclude': '**/node_modules/**' } ], 'test': { 'karma': { 'config': './karma.conf.js' } }, 'defaults': { 'styleExt': 'css', 'component': {} } }
Sitten meidän on oltava varmoja siitä, että tämä resurssi kopioidaan käyttöönotettuun kansioon. Ainoa mitä meidän on tehtävä, on muuttaa Angular CLI -asetustiedostomme AuthGuard
:
canActivateAuthGuard.ts
Muistatko, kuinka meillä oli luokka helpers
toteutettu reititysmääritysten asettamiseksi? Aina kun siirrymme toiselle sivulle, käytämme tätä luokkaa tarkistamaan, onko käyttäjä todennettu tunnuksella. Jos ei, ohjaamme sinut automaattisesti sisäänkirjautumissivulle. Tätä varten tiedosto on import { CanActivate, Router } from '@angular/router'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Helpers } from './helpers'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router, private helper: Helpers) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | boolean { if (!this.helper.isAuthenticated()) { this.router.navigate(['/login']); return false; } return true; } }
- luo se canActivate
: n sisällä kansion ja näyttää siltä tältä:
Router
Joten joka kerta, kun vaihdamme sivua, menetelmä Helper
kutsutaan, mikä tarkistaa, onko käyttäjä todennettu, ja jos ei, käytämme helpers
esimerkiksi ohjata sisäänkirjautumissivulle. Mutta mikä tämä uusi menetelmä on helpers.ts
luokka? localStorage
-Kohdan alla luodaan tiedosto localStorage
. Täällä meidän on hallittava sessionStorage
, johon tallennamme takapäästä saamamme tunnuksen.
Huomautus
sessionStorage : Ssa voit myös käyttää evästeitä tai localStorage , ja päätös riippuu käytöksestä, jonka haluamme toteuttaa. Kuten nimestä voi päätellä, sessionStorage on käytettävissä vain selainistunnon ajan ja poistetaan, kun välilehti tai ikkuna suljetaan; se kuitenkin selviää sivujen uudelleenlatauksista. Jos tallentamiesi tietojen on oltava jatkuvasti käytettävissä, niin localStorage on parempi kuin import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Subject } from 'rxjs/Subject'; @Injectable() export class Helpers { private authenticationChanged = new Subject(); constructor() { } public isAuthenticated():boolean public isAuthenticationChanged():any { return this.authenticationChanged.asObservable(); } public getToken():any { if( window.localStorage['token'] === undefined || window.localStorage['token'] === null || window.localStorage['token'] === 'null' || window.localStorage['token'] === 'undefined' || window.localStorage['token'] === '') { return ''; } 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: any):void { window.localStorage['token'] = value; this.authenticationChanged.next(this.isAuthenticated()); } } Evästeet on tarkoitettu ensisijaisesti palvelinpuolen lukemiseen, kun taas Subject voidaan lukea vain asiakaspuolella. Joten kysymys on, kuka tarvitsee sovelluksessasi näitä tietoja - asiakas tai palvelin? |
{ path: 'logout', component: LogoutComponent},
Onko todentamiskoodillamme järkevää nyt? Palataan localStorage
-sivulle luokan myöhemmin, mutta nyt kierretään hetkeksi takaisin reititysmäärityksiin. Katsokaa tätä riviä:
components/login
Tämä on komponenttimme kirjautua ulos sivustosta, ja se on vain yksinkertainen luokka logout.component.ts
: n puhdistamiseen. Luodaan se import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Helpers } from '../../helpers/helpers'; @Component({ selector: 'app-logout', template:'' }) export class LogoutComponent implements OnInit { constructor(private router: Router, private helpers: Helpers) { } ngOnInit() { this.helpers.logout(); this.router.navigate(['/login']); } }
-kohdassa kansio, jonka nimi on /logout
:
localStorage
Joten joka kerta kun siirrymme URL-osoitteeseen login.component.ts
, import { Component, OnInit } from '@angular/core'; 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 class LoginComponent implements OnInit { constructor(private helpers: Helpers, private 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']); }); } }
poistetaan ja sivusto ohjaa sisäänkirjautumissivulle. Lopuksi, luodaan app.component.ts
kuten tämä:
export class AppComponent implements AfterViewInit { subscription: Subscription; authentication: boolean; constructor(private helpers: Helpers) { } ngAfterViewInit() { this.subscription = this.helpers.isAuthenticationChanged().pipe( startWith(this.helpers.isAuthenticated()), delay(0)).subscribe((value) => this.authentication = value ); } title = 'Angular 5 Seed'; ngOnDestroy() { this.subscription.unsubscribe(); } }
Kuten näette, olemme toistaiseksi koodanneet valtakirjamme täältä. Huomaa, että tässä kutsumme palveluluokkaa; Luomme nämä palveluluokat, jotta pääsemme käyttöliittymään seuraavassa osassa.
Lopuksi meidän on palattava takaisin Subject
-sivulle tiedosto, sivuston asettelu. Jos käyttäjä on todennettu, se näyttää valikko- ja otsikko-osiot, mutta jos ei, asettelua muutetaan näyttämään vain kirjautumissivumme.
Observable
Muista Observable
luokka auttajaluokassamme? Tämä on authentication
. app.component.html
s tarjoavat tukea viestien lähettämiseen julkaisijoiden ja tilaajien välillä sovelluksessasi. Aina kun todennustunnus muuttuu, Menu
ominaisuus päivitetään. Tarkastellaan services
tiedosto, sillä on todennäköisesti järkevämpää nyt:
token.service.ts
Tässä vaiheessa olemme siirtymässä eri sivuille, todentamassa asiakkaan puolta ja tekemällä hyvin yksinkertaisen asettelun. Mutta miten voimme saada tietoja takapäästä? Suosittelen voimakkaasti, että kaikki taustakäyttö tehdään palvelu luokkiin. Ensimmäinen palvelumme on 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 class TokenService extends BaseService { private pathAPI = this.config.setting['PathAPI']; public errorMessage: string; constructor(private http: HttpClient, private config: AppConfig, helper: Helpers) { super(helper); } auth(data: any): any { let body = JSON.stringify(data); return this.getToken(body); } private getToken (body: any): Observable { return this.http.post(this.pathAPI + 'token', body, super.header()).pipe( catchError(super.handleError) ); } }
-palvelun sisällä kansio, nimeltään TokenService
:
BaseService
Ensimmäinen kutsu takapäähän on POST-kutsu tunnussovellusliittymään. Tunnussovellusliittymä ei tarvitse tunnussarjaa otsikossa, mutta mitä tapahtuu, jos kutsumme toisen päätepisteen? Kuten voit nähdä täältä, 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 { Helpers } from '../helpers/helpers'; @Injectable() export class BaseService { constructor(private helper: Helpers) { } public extractData(res: Response) { let body = res.json(); return body || {}; } public handleError(error: Response | any) { // In a real-world app, we might use a remote logging infrastructure let errMsg: string; if (error instanceof Response) { const body = error.json() || ''; const err = body || JSON.stringify(body); errMsg = `${error.status} - $error.statusText ${err}`; } else { errMsg = error.message ? error.message : error.toString(); } console.error(errMsg); return Observable.throw(errMsg); } public header() { let header = new HttpHeaders({ 'Content-Type': 'application/json' }); if(this.helper.isAuthenticated()) { header = header.append('Authorization', 'Bearer ' + this.helper.getToken()); } return { headers: header }; } public setToken(data:any) { this.helper.setToken(data); } public failToken(error: Response | any) { this.helper.failToken(); return this.handleError(Response); } }
(ja palveluluokat yleensä) perivät super.header
luokassa. Katsotaanpa tätä:
localStorage
Joten joka kerta, kun soitamme HTTP-puhelun, toteutamme pyynnön otsikon vain user.ts
: lla. Jos tunnus on export class User { id: number; name: string; }
sitten se liitetään otsikkoon, mutta jos ei, asetamme vain JSON-muodon. Toinen asia, jonka voimme nähdä tässä, on se, mitä tapahtuu, jos todennus epäonnistuu.
Kirjautumiskomponentti soittaa palveluluokkaan ja palveluluokka soittaa loppupäähän. Kun tunnus on saatu, auttajaluokka hallitsee tunnusta, ja nyt olemme valmiita hakemaan luettelon käyttäjistä tietokannastamme.
Saadaksesi tietoja tietokannasta, varmista ensin, että sovitamme malliluokat vastauksessamme taustanäkymämallien kanssa.
Sisään user.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 { BaseService } from './base.service'; import { User } from '../models/user'; import { AppConfig } from '../config/config'; import { Helpers } from '../helpers/helpers'; @Injectable() export class UserService extends BaseService { private pathAPI = this.config.setting['PathAPI']; constructor(private http: HttpClient, private config: AppConfig, helper: Helpers) { super(helper); } /** GET heroes from the server */ getUsers (): Observable { return this.http.get(this.pathAPI + 'user', super.header()).pipe( catchError(super.handleError)); }
Ja voimme nyt luoda SeedAPI.Web.API
tiedosto:
Web.API
Tervetuloa Web API Core 2 -sovelluksen ensimmäiseen vaiheeseen. Ensimmäinen asia, mitä tarvitsemme, on luoda ASP.Net-ydinverkkosovellus, jota kutsumme ViewModels
.
Muista valita Tyhjä-malli puhtaaseen alkuun, kuten näet alla:
Siinä kaikki, luomme ratkaisun tyhjällä verkkosovelluksella. Nyt arkkitehtuurimme on sellainen kuin alla luetellaan, joten meidän on luotava erilaiset projektit:
Voit tehdä tämän napsauttamalla vain jokaisen kohdalla hiiren kakkospainikkeella ratkaisua ja lisäämällä 'Class Library (.NET Core)' -projektin.
Edellisessä osiossa loimme kahdeksan projektia, mutta mihin ne on tarkoitettu? Tässä on yksinkertainen kuvaus kustakin:
Interfaces
: Tämä on käynnistysprojektimme ja missä päätepisteet luodaan. Tässä asetamme JWT: n, ruiskutusriippuvuudet ja ohjaimet.Commons
: Tässä suoritetaan muunnokset tietotyypistä, jonka ohjaimet palauttavat vastauksissa käyttöliittymään. On hyvä tapa sovittaa nämä luokat etupään malleihin.Models
: Tästä on apua injektointiriippuvuuksien toteuttamisessa. Staattisesti kirjoitetun kielen pakottava etu on, että kääntäjä voi auttaa varmistamaan, että koodi, johon koodi perustuu, tosiasiallisesti täytetään.ViewModels
: Kaikki jaetut toiminnot ja apuohjelmakoodi ovat täällä.Models
: On hyvä käytäntö olla sovittamatta tietokantaa suoraan käyttöliittymän suuntaan Maps
, joten ViewModels
on luoda etupäästä riippumaton entiteettitietokantaluokka. Tämä antaa meille mahdollisuuden tulevaisuudessa muuttaa tietokantaamme ilman, että sillä on välttämättä vaikutusta käyttöliittymään. Se auttaa myös silloin, kun haluamme yksinkertaisesti tehdä joitakin korjauksia.Models
: Tässä kartoitamme Services
kohteeseen Repositories
ja päinvastoin. Tätä vaihetta kutsutaan ohjaimien ja palveluiden välillä.App_Start
: Kirjasto koko liiketoimintalogiikan tallentamiseen.JwtTokenConfig.cs
: Tämä on ainoa paikka, jossa kutsumme tietokantaa.Viitteet näyttävät tältä:
Tässä osiossa näemme tunnuksen todennuksen peruskokoonpanon ja menemme hieman syvemmälle turvallisuuden aiheeseen.
Aloita JSON-verkkotunnuksen (JWT) asettaminen luomalla seuraava luokka namespace SeedAPI.Web.API.App_Start { public class JwtTokenConfig { public static void AddAuthentication(IServiceCollection services, IConfiguration configuration) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = configuration['Jwt:Issuer'], ValidAudience = configuration['Jwt:Issuer'], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration['Jwt:Key'])) }; services.AddCors(); }); } } }
: n sisällä kansio nimeltä appsettings.json
. Sisällä oleva koodi näyttää tältä:
'Jwt': { 'Key': 'veryVerySecretKey', 'Issuer': 'http://localhost:50498/' }
Vahvistusparametrien arvot riippuvat kunkin projektin vaatimuksista. Voimassa oleva käyttäjä ja yleisö, jonka voimme asettaa lukemaan määritystiedoston ConfigureServices
:
startup.cs
Sitten meidän tarvitsee kutsua sitä vain // This method gets called 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(); }
: sta menetelmä TokenController.cs
:
appsettings.json
Nyt olemme valmiita luomaan ensimmäisen ohjaimen nimeltä 'veryVerySecretKey'
. Asetettu arvo LoginViewModel
kohteeseen ViewModels
pitäisi olla sama kuin käytämme tunnuksen luomiseen, mutta ensin luodaan namespace SeedAPI.ViewModels { public class LoginViewModel : IBaseViewModel { public string username { get; set; } public string password { get; set; } } }
sisällä namespace SeedAPI.Web.API.Controllers { [Route('api/Token')] public class 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 != null) { var tokenString = BuildToken(user); response = Ok(new { token = tokenString }); } return response; } private string BuildToken(UserViewModel user) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config['Jwt:Key'])); 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); } private UserViewModel Authenticate(LoginViewModel login) { UserViewModel user = null; if (login.username == 'pablo' && login.password == 'secret') { user = new UserViewModel { name = 'Pablo' }; } return user; } } }
projekti:
BuildToken
Ja lopuksi ohjain:
Authenticate
identityDbContext
menetelmä luo tunnuksen annetulla turvakoodilla. Models
methodilla on juuri käyttäjän vahvistaminen tällä hetkellä kovakoodattu, mutta meidän on kutsuttava tietokanta vahvistamaan se lopulta.
Entity Frameworkin asentaminen on todella helppoa, koska Microsoft lanseerasi Core 2.0 -version - EF-ydin 2 lyhyesti. Aiomme perehtyä syvemmälle koodin ensimmäinen malli, joka käyttää Context
, joten varmista ensin, että olet asentanut kaikki riippuvuudet. NuGetin avulla voit hallita sitä:
ApplicationContext.cs
voimme luoda täällä IApplicationContext.cs
kansioon kaksi tiedostoa, EntityBase
ja EntityBase
. Tarvitsemme myös User.cs
luokassa.
IdentityUser
kukin entiteettimalli perii tiedostot, mutta namespace SeedAPI.Models { public class User : IdentityUser { public string Name { get; set; } } }
on identiteettiluokka ja ainoa entiteetti, joka perii namespace SeedAPI.Models.EntityBase { public class EntityBase { public DateTime? Created { get; set; } public DateTime? Updated { get; set; } public bool Deleted { get; set; } public EntityBase() { Deleted = false; } public virtual int IdentityID() { return 0; } public virtual object[] IdentityID(bool dummy = true) { return new List().ToArray(); } } }
Alla ovat molemmat luokat:
ApplicationContext.cs
namespace SeedAPI.Models.Context { public class ApplicationContext : IdentityDbContext, IApplicationContext { private IDbContextTransaction dbContextTransaction; public ApplicationContext(DbContextOptions options) : base(options) { } public DbSet UsersDB { get; set; } public new void SaveChanges() { base.SaveChanges(); } public new DbSet Set() where T : class { return base.Set(); } public void BeginTransaction() { dbContextTransaction = Database.BeginTransaction(); } public void CommitTransaction() { if (dbContextTransaction != null) { dbContextTransaction.Commit(); } } public void RollbackTransaction() { if (dbContextTransaction != null) { dbContextTransaction.Rollback(); } } public void DisposeTransaction() { if (dbContextTransaction != null) { dbContextTransaction.Dispose(); } } } }
Nyt olemme valmiita luomaan App_Start
, joka näyttää tältä:
Web.API
Olemme todella lähellä, mutta ensin meidän on luotava lisää luokkia, tällä kertaa namespace SeedAPI.Web.API.App_Start { public class 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(options => options.UseSqlServer(configuration.GetConnectionString('DefaultConnection'))); } } }
namespace SeedAPI.Web.API.App_Start { public class 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 = 2, name = 'Diego' }); } } }
-kansiossa oleva kansio projekti. Ensimmäinen luokka on alustaa sovelluskonteksti ja toinen on luoda näytetietoja vain testausta varten kehityksen aikana.
// This method gets called 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(); } // ... // This method gets called by the runtime. Use this method to configure the HTTP request 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(); }
App_Start
Ja kutsumme heitä käynnistystiedostostamme:
DependencyInjectionConfig.cs
On hyvä käytäntö käyttää riippuvuusinjektiota eri projektien välillä liikkumiseen. Tämä auttaa meitä kommunikoimaan ohjainten ja kartoittajien, kartoittajien ja palvelujen sekä palveluiden ja arkistojen välillä.
Kansiossa namespace SeedAPI.Web.API.App_Start { public class DependencyInjectionConfig { public static void AddScope(IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } } }
luomme tiedoston Map
ja se näyttää tältä:
Service
Meidän on luotava jokaiselle uudelle entiteetille uusi Repository
, startup.cs
ja // This method gets called 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(); }
ja sovitettava ne tähän tiedostoon. Sitten meidän on vain soitettava sille namespace SeedAPI.Web.API.Controllers { [Route('api/[controller]')] [Authorize] public class UserController : Controller { IUserMap userMap; public UserController(IUserMap map) { userMap = map; } // GET api/user [HttpGet] public IEnumerable Get() { return userMap.GetAll(); ; } // GET api/user/5 [HttpGet('{id}')] public string Get(int id) { return 'value'; } // POST api/user [HttpPost] public void Post([FromBody]string 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) { } } }
tiedosto:
Authorize
Lopuksi, kun meidän on saatava käyttäjäluettelo tietokannasta, voimme luoda ohjaimen käyttämällä tätä riippuvuusinjektiota:
Map
Katso miten Maps
attribute on täällä, jotta voidaan varmistaa, että käyttöliittymä on kirjautunut sisään ja miten riippuvuuden ruiskutus toimii luokan konstruktorissa.
Meillä on vihdoin kutsu tietokantaan, mutta ensin meidän on ymmärrettävä ViewModels
projekti.
UserMap.cs
ProjektiTämä vaihe on vain kartoittaa namespace SeedAPI.Maps { public class UserMap : IUserMap { IUserService userService; public UserMap(IUserService service) { userService = service; } public UserViewModel Create(UserViewModel viewModel) { User user = ViewModelToDomain(viewModel); return DomainToViewModel(userService.Create(user)); } public bool Update(UserViewModel viewModel) { User user = ViewModelToDomain(viewModel); return userService.Update(user); } public bool Delete(int id) { return userService.Delete(id); } public List GetAll() { return DomainToViewModel(userService.GetAll()); } public UserViewModel DomainToViewModel(User domain) { UserViewModel model = new UserViewModel(); model.name = domain.Name; return model; } public List DomainToViewModel(List domain) { List model = new List(); foreach (User of in domain) { model.Add(DomainToViewModel(of)); } return model; } public User ViewModelToDomain(UserViewModel officeViewModel) { User domain = new User(); domain.Name = officeViewModel.name; return domain; } } }
tietokantamalleihin ja niistä. Meidän on luotava yksi kullekin entiteetille ja edellisen esimerkkimme mukaisesti Services
tiedosto näyttää tältä:
namespace SeedAPI.Services { public class UserService : IUserService { private IUserRepository repository; public UserService(IUserRepository userRepository) { repository = userRepository; } public User Create(User domain) { return repository.Save(domain); } public bool Update(User domain) { return repository.Update(domain); } public bool Delete(int id) { return repository.Delete(id); } public List GetAll() { return repository.GetAll(); } } }
Näyttää siltä, että jälleen kerran riippuvuuden injektointi toimii luokan rakentajalla, joka yhdistää Mapsin Services-projektiin.
Repositories
ProjektiTässä ei ole liikaa sanottavaa: Esimerkkimme on todella yksinkertainen, eikä meillä ole liiketoimintalogiikkaa tai -koodia kirjoittaa tähän. Tämä projekti osoittautuu hyödylliseksi tulevissa vaativissa vaatimuksissa, kun meidän on laskettava tai tehtävä logiikkaa ennen tietokannan tai ohjaimen vaiheita tai niiden jälkeen. Esimerkin mukaan luokka näyttää melko paljaalta:
UserRepository.cs
namespace SeedAPI.Repositories { public class UserRepository : BaseRepository, IUserRepository { public UserRepository(IApplicationContext context) : base(context) { } public User Save(User domain) { try { var us = InsertUser(domain); return us; } catch (Exception ex) { //ErrorManager.ErrorHandler.HandleError(ex); throw ex; } } public bool Update(User domain) { try { //domain.Updated = DateTime.Now; UpdateUser(domain); return true; } catch (Exception ex) { //ErrorManager.ErrorHandler.HandleError(ex); throw ex; } } public bool Delete(int id) { try { User user = Context.UsersDB.Where(x => x.Id.Equals(id)).FirstOrDefault(); if (user != null) { //Delete(user); return true; } else { return false; } } catch (Exception ex) { //ErrorManager.ErrorHandler.HandleError(ex); throw ex; } } public List GetAll() { try { return Context.UsersDB.OrderBy(x => x.Name).ToList(); } catch (Exception ex) { //ErrorManager.ErrorHandler.HandleError(ex); throw ex; } } } }
ProjektiOlemme siirtymässä tämän opetusohjelman viimeiseen osioon: Meidän on vain soitettava puheluita tietokantaan, joten luomme
|_+_|tiedosto, josta voimme lukea, lisätä tai päivittää käyttäjiä tietokantaan.
|_+_|
Tässä artikkelissa selitin, kuinka luoda hyvä arkkitehtuuri Angular 5: n ja Web API Core 2: n avulla. Tässä vaiheessa olet luonut perustan suurelle projektille, jonka koodi tukee vaatimusten suurta kasvua.
Totuus on, mikään ei kilpaile Java-käyttöjärjestelmän kanssa käyttöliittymässä, ja mikä voi kilpailla C #: n kanssa, jos tarvitset SQL Serverin ja Entity Frameworkin tukea takapäässä? Joten tämän artikkelin idea oli yhdistää kahden maailman parhaat puolet, ja toivon, että olet nauttinut siitä.
Jos työskentelet ryhmässä Kulmalliset kehittäjät luultavasti etu- ja takapäässä voi olla erilaisia kehittäjiä, joten hyvä idea synkronoida molempien joukkueiden ponnistelut voi olla integroida Swagger Web API 2: een. Swagger on loistava työkalu RESTFul-sovellusliittymiesi dokumentointiin ja testaamiseen. Lue Microsoft-opas: Aloita Swashbuckle ja ASP.NET Core .
Jos olet vielä hyvin uusi Angular 5: ssä ja sinulla on vaikeuksia seurata, lue Angular 5 -opetusohjelma: Vaiheittainen opas ensimmäiseen Angular 5 -sovellukseesi kaveri ApeeScapeer Sergey Moiseev.
Voit käyttää kumpaakin IDE: tä käyttöliittymässä, monet ihmiset haluavat liittyä käyttöliittymään Web-sovelluskirjastoon ja automatisoida käyttöönoton. Pidän parempana pitää käyttöliittymäkoodi erillään taustasta ja löytänyt Visual Studio -koodin todella hyväksi työkaluksi, erityisesti älykäs, kanssa Typescript-koodille.
Kulmamateriaali on käyttöliittymäkomponenttikehys, jota ei tarvitse käyttää. UI-komponenttikehykset auttavat meitä järjestämään ulkoasun ja reagoivan verkkosivustolla, mutta meillä on paljon niitä markkinoilla, kuten bootstrap ja muut, ja voimme valita haluamasi ulkoasun.
Kulmareititin mahdollistaa navigoinnin yhdestä näkymästä toiseen, kun käyttäjät suorittavat sovellustehtäviä. Se voi tulkita selaimen URL-osoitteen ohjeeksi siirtyä asiakkaan luomaan näkymään. Kehittäjä saa asettaa URL-osoitteita, parametreja ja voimme saastuttaa CanAuthenticate-sovelluksella, että voimme vahvistaa käyttäjän autentikaation
Usein luokat tarvitsevat pääsyn toisiinsa, ja tämä suunnittelumalli osoittaa, kuinka luodaan löyhästi kytkettyjä luokkia. Kun kaksi luokkaa on tiukasti kytketty, ne ovat yhteydessä binääriseen assosiaatioon.
JSON Web Token (JWT) on kompakti kirjasto tietojen turvalliseen lähettämiseen osapuolten välillä JSON-objektina. JWT: t voidaan salata myös salassapitoon osapuolten välillä, keskitymme allekirjoitettuihin tunnuksiin.