Σκέφτομαι να γράψω μια ανάρτηση ιστολογίου από την πρώτη έκδοση του Angular σκότωσε πρακτικά τη Microsoft από την πλευρά του πελάτη. Τεχνολογίες όπως το ASP.Net, οι φόρμες Web και το MVC Razor έχουν καταστεί άνευ αντικειμένου, αντικαταστάθηκαν από ένα πλαίσιο JavaScript που δεν είναι ακριβώς η Microsoft. Ωστόσο, από τη δεύτερη έκδοση του Angular, η Microsoft και η Google συνεργάζονται για τη δημιουργία του Angular 2, και αυτό είναι όταν οι δύο αγαπημένες μου τεχνολογίες άρχισαν να συνεργάζονται.
Σε αυτό το blog, θέλω να βοηθήσω τους ανθρώπους να δημιουργήσουν την καλύτερη αρχιτεκτονική που συνδυάζει αυτούς τους δύο κόσμους. Είσαι έτοιμος? Ορίστε!
Θα δημιουργήσετε έναν πελάτη Angular 5 που καταναλώνει μια υπηρεσία RESTful Web API Core 2.
Η πλευρά του πελάτη:
Η πλευρά του διακομιστή:
Σημείωση
Σε αυτήν την ανάρτηση ιστολογίου υποθέτουμε ότι ο αναγνώστης έχει ήδη βασικές γνώσεις για TypeScript, γωνιακές ενότητες, στοιχεία και εισαγωγή / εξαγωγή. Ο στόχος αυτής της ανάρτησης είναι να δημιουργήσει μια καλή αρχιτεκτονική που θα επιτρέψει την ανάπτυξη του κώδικα με την πάροδο του χρόνου. |
Ας ξεκινήσουμε επιλέγοντας το IDE. Φυσικά, αυτή είναι μόνο η προτίμησή μου και μπορείτε να χρησιμοποιήσετε αυτήν που νιώθετε πιο άνετα. Στην περίπτωσή μου, θα χρησιμοποιήσω τον κώδικα Visual Studio και το Visual Studio 2017.
Γιατί δύο διαφορετικά IDE; Δεδομένου ότι η Microsoft δημιούργησε τον Visual Studio Code για τη διεπαφή, δεν μπορώ να σταματήσω να χρησιμοποιώ αυτό το IDE. Τέλος πάντων, θα δούμε επίσης πώς να ενσωματώσουμε το Angular 5 στο έργο λύσης, το οποίο θα σας βοηθήσει εάν είστε το είδος προγραμματιστή που προτιμά να κάνει εντοπισμό σφαλμάτων τόσο πίσω όσο και εμπρός με ένα μόνο F5.
Σχετικά με το πίσω μέρος, μπορείτε να εγκαταστήσετε την τελευταία έκδοση του Visual Studio 2017, η οποία διαθέτει δωρεάν έκδοση για προγραμματιστές, αλλά είναι πολύ πλήρης: Κοινότητα.
Λοιπόν, εδώ είναι η λίστα με τα πράγματα που πρέπει να εγκαταστήσουμε για αυτό το σεμινάριο:
Σημείωση
Βεβαιωθείτε ότι εκτελείτε τουλάχιστον τον κόμβο 6.9.x και npm 3.x.x εκτελώντας node -v και npm -v σε παράθυρο τερματικού ή κονσόλας. Οι παλαιότερες εκδόσεις προκαλούν σφάλματα, αλλά οι νεότερες εκδόσεις είναι εντάξει. |
Αφήστε τη διασκέδαση να ξεκινήσει! Το πρώτο πράγμα που πρέπει να κάνουμε είναι να εγκαταστήσουμε το Angular CLI παγκοσμίως, οπότε ανοίξτε τη γραμμή εντολών node.js και εκτελέστε αυτήν την εντολή:
npm install -g @angular/cli
Εντάξει, τώρα έχουμε το πρόγραμμα δέσμης στοιχείων μας. Αυτό συνήθως εγκαθιστά τη λειτουργική μονάδα στον φάκελο χρήστη. Ένα ψευδώνυμο δεν θα πρέπει να είναι απαραίτητο από προεπιλογή, αλλά εάν το χρειάζεστε, μπορείτε να εκτελέσετε την επόμενη γραμμή:
alias ng='/.npm/lib/node_modules/angular-cli/bin/ng'
Το επόμενο βήμα είναι να δημιουργήσετε το νέο έργο. Θα το ονομάσω angular5-app
. Αρχικά, μεταβαίνουμε στον φάκελο στον οποίο θέλουμε να δημιουργήσουμε τον ιστότοπο και μετά:
ng new angular5-app
Ενώ μπορείτε να δοκιμάσετε τον νέο ιστότοπό σας που τρέχει μόλις ng serve --open
, προτείνω να δοκιμάσετε τον ιστότοπο από την αγαπημένη σας υπηρεσία ιστού. Γιατί; Λοιπόν, ορισμένα ζητήματα μπορούν να συμβούν μόνο στην παραγωγή και την κατασκευή του ιστότοπου με ng build
είναι ο πλησιέστερος τρόπος προσέγγισης αυτού του περιβάλλοντος. Τότε μπορούμε να ανοίξουμε το φάκελο angular5-app
με κώδικα Visual Studio και εκτέλεση ng build
στο τερματικό bash:
Ένας νέος φάκελος που ονομάζεται dist
θα δημιουργηθεί και μπορούμε να το εξυπηρετήσουμε χρησιμοποιώντας IIS ή οποιονδήποτε διακομιστή ιστού προτιμάτε. Στη συνέχεια, μπορείτε να πληκτρολογήσετε τη διεύθυνση URL στο πρόγραμμα περιήγησης και… τελειώσατε!
σε ποια γλώσσα είναι γραμμένα τα windows
Σημείωση
Δεν είναι ο σκοπός αυτού του σεμιναρίου να δείξει πώς να δημιουργήσετε έναν διακομιστή ιστού, επομένως υποθέτω ότι έχετε ήδη αυτές τις γνώσεις. |
src
Ντοσιέ
My src
ο φάκελος είναι δομημένος ως εξής: Μέσα στο app
φάκελο που έχουμε components
όπου θα δημιουργήσουμε για κάθε γωνιακό στοιχείο τα css
, ts
, spec
και html
αρχεία. Θα δημιουργήσουμε επίσης ένα config
φάκελο για να διατηρήσετε τη διαμόρφωση του ιστότοπου, directives
θα έχει όλες τις προσαρμοσμένες οδηγίες μας, helpers
θα φιλοξενήσει κοινό κώδικα όπως ο διαχειριστής ελέγχου ταυτότητας, layout
θα περιέχει τα κύρια συστατικά όπως το σώμα, το κεφάλι και τα πλαϊνά πάνελ, models
διατηρεί αυτό που θα ταιριάζει με τα μοντέλα οπίσθιας προβολής και τέλος services
θα έχει τον κωδικό για όλες τις κλήσεις προς το πίσω μέρος.
Έξω από το app
φάκελος θα διατηρήσουμε τους φακέλους που έχουν δημιουργηθεί από προεπιλογή, όπως assets
και environments
, και επίσης τα ριζικά αρχεία.
Ας δημιουργήσουμε ένα config.ts
αρχείο μέσα στο config
φάκελο και καλέστε την τάξη AppConfig
. Εδώ μπορούμε να ορίσουμε όλες τις τιμές που θα χρησιμοποιήσουμε σε διαφορετικά μέρη του κώδικα μας. για παράδειγμα, το URL του API. Σημειώστε ότι η κλάση εφαρμόζει ένα get
ιδιότητα που λαμβάνει, ως παράμετρο, μια δομή κλειδιού / τιμής και μια απλή μέθοδο για να αποκτήσετε πρόσβαση στην ίδια τιμή. Με αυτόν τον τρόπο, θα είναι εύκολο να λάβετε τις τιμές απλώς κλήση this.config.setting['PathAPI']
από τις τάξεις που κληρονομούν από αυτό.
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]; } };
Πριν ξεκινήσετε τη διάταξη, ας ρυθμίσουμε το πλαίσιο στοιχείου διεπαφής χρήστη. Φυσικά, μπορείτε να χρησιμοποιήσετε άλλους όπως το Bootstrap, αλλά αν σας αρέσει το στυλ του Υλικού, το προτείνω γιατί υποστηρίζεται επίσης από την Google.
Για να το εγκαταστήσουμε, πρέπει απλώς να εκτελέσουμε τις επόμενες τρεις εντολές, τις οποίες μπορούμε να εκτελέσουμε στο τερματικό του Visual Studio Code:
npm install --save @angular/material @angular/cdk npm install --save @angular/animations npm install --save hammerjs
Η δεύτερη εντολή είναι επειδή ορισμένα στοιχεία Υλικού εξαρτώνται από τις Γωνιακές κινούμενες εικόνες. Συνιστώ επίσης την ανάγνωση την επίσημη σελίδα για να κατανοήσουμε ποια προγράμματα περιήγησης υποστηρίζονται και τι είναι το polyfill.
Η τρίτη εντολή είναι επειδή ορισμένα στοιχεία Υλικού βασίζονται στο HammerJS για χειρονομίες.
Τώρα μπορούμε να προχωρήσουμε στην εισαγωγή των λειτουργικών μονάδων που θέλουμε να χρησιμοποιήσουμε στο app.module.ts
αρχείο:
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 ],
Το επόμενο βήμα είναι να αλλάξετε το style.css
αρχείο, προσθέτοντας το είδος του θέματος που θέλετε να χρησιμοποιήσετε:
@import ' [email protected] /material/prebuilt-themes/deeppurple-amber.css';
Τώρα εισαγάγετε HammerJS προσθέτοντας αυτήν τη γραμμή στο main.ts
αρχείο:
import 'hammerjs';
Και τέλος, το μόνο που μας λείπει είναι να προσθέσουμε τα εικονίδια Υλικού στο index.html
, στην ενότητα κεφαλίδας:
layout
Σε αυτό το παράδειγμα, θα δημιουργήσουμε μια απλή διάταξη όπως αυτή:
Η ιδέα είναι να ανοίξετε / αποκρύψετε το μενού κάνοντας κλικ σε κάποιο κουμπί στην κεφαλίδα. Το Angular Responsive θα κάνει το υπόλοιπο έργο για εμάς. Για να το κάνουμε αυτό θα δημιουργήσουμε ένα app.component
φάκελο και βάλτε μέσα του το app.component
αρχεία που δημιουργήθηκαν από προεπιλογή. Αλλά θα δημιουργήσουμε επίσης τα ίδια αρχεία για κάθε ενότητα της διάταξης όπως μπορείτε να δείτε στην επόμενη εικόνα. Τότε, head.component
θα είναι το σώμα, left-panel.component
η κεφαλίδα και app.component.html
το μενού.
Τώρα ας αλλάξουμε Menu
ως εξής:
authentication
Βασικά θα έχουμε ένα head.component.html
ιδιότητα στο στοιχείο που θα μας επιτρέψει να αφαιρέσουμε την κεφαλίδα και το μενού εάν ο χρήστης δεν είναι συνδεδεμένος και, αντίθετα, να εμφανίσει μια απλή σελίδα σύνδεσης.
Το Logout!
μοιάζει με αυτό:
left-panel.component.html
Απλώς ένα κουμπί για να αποσυνδεθεί ο χρήστης - θα επανέλθουμε σε αυτό ξανά αργότερα. Όσο για Dashboard Users
, προς το παρόν αλλάξτε απλώς το HTML σε:
import { Component } from '@angular/core'; @Component({ selector: 'app-head', templateUrl: './head.component.html', styleUrls: ['./head.component.css'] }) export class HeadComponent { title = 'Angular 5 Seed'; }
Το κρατήσαμε απλό: Μέχρι στιγμής είναι μόνο δύο σύνδεσμοι για πλοήγηση σε δύο διαφορετικές σελίδες. (Θα επιστρέψουμε επίσης σε αυτό αργότερα.)
Τώρα, έτσι μοιάζουν τα αρχεία TypeScript της κεφαλής και της αριστερής πλευράς:
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
Τι γίνεται όμως με τον κωδικό TypeScript για app
; Θα αφήσουμε ένα μικρό μυστήριο εδώ και θα το σταματήσουμε για λίγο και θα επανέλθουμε σε αυτό μετά την εφαρμογή του ελέγχου ταυτότητας.
Εντάξει, τώρα έχουμε Angular Material που μας βοηθά με το περιβάλλον εργασίας χρήστη και μια απλή διάταξη για να αρχίσουμε να δημιουργούμε τις σελίδες μας. Αλλά πώς μπορούμε να περιηγηθούμε μεταξύ σελίδων;
Για να δημιουργήσουμε ένα απλό παράδειγμα, ας δημιουργήσουμε δύο σελίδες: 'Χρήστης', όπου μπορούμε να λάβουμε μια λίστα με τους υπάρχοντες χρήστες στη βάση δεδομένων και 'Πίνακας ελέγχου', μια σελίδα όπου μπορούμε να δείξουμε ορισμένα στατιστικά στοιχεία.
Μέσα στο app-routing.modules.ts
φάκελο θα δημιουργήσουμε ένα αρχείο που ονομάζεται 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 {}
μοιάζει με αυτό:
RouterModule
Είναι τόσο απλό: Απλώς εισαγωγή Routes
και @angular/router
από /dashboard
, μπορούμε να χαρτογραφήσουμε τις διαδρομές που θέλουμε να εφαρμόσουμε. Εδώ δημιουργούμε τέσσερις διαδρομές:
/login
: Η αρχική μας σελίδα/logout
: Η σελίδα στην οποία ο χρήστης μπορεί να πραγματοποιήσει έλεγχο ταυτότητας/users
: Μια απλή διαδρομή για την αποσύνδεση του χρήστηdashboard
: Η πρώτη μας σελίδα όπου θέλουμε να παραθέσουμε τους χρήστες από το πίσω μέροςΣημειώστε ότι /
είναι η σελίδα μας από προεπιλογή, οπότε αν ο χρήστης πληκτρολογήσει τη διεύθυνση URL canActivate
, η σελίδα θα ανακατευθύνει αυτόματα σε αυτήν τη σελίδα. Επίσης, ρίξτε μια ματιά στο AuthGuard
παράμετρος: Εδώ δημιουργούμε μια αναφορά στην κλάση left-panel.component.html
, η οποία θα μας επιτρέψει να ελέγξουμε εάν ο χρήστης είναι συνδεδεμένος. Εάν όχι, ανακατευθύνει στη σελίδα σύνδεσης. Στην επόμενη ενότητα, θα σας δείξω πώς να δημιουργήσετε αυτήν την τάξη.
Τώρα, το μόνο που πρέπει να κάνουμε είναι να δημιουργήσουμε το μενού. Θυμηθείτε στην ενότητα διάταξης όταν δημιουργήσαμε το Dashboard Users
αρχείο για να μοιάζει με αυτό;
our.site.url/users
Εδώ είναι όπου ο κώδικάς μας συναντά την πραγματικότητα. Τώρα μπορούμε να δημιουργήσουμε τον κώδικα και να τον δοκιμάσουμε στη διεύθυνση URL: Θα πρέπει να μπορείτε να πλοηγηθείτε από τη σελίδα του πίνακα ελέγχου στους χρήστες, αλλά τι θα συμβεί αν πληκτρολογήσετε τη διεύθυνση URL http://www.mysite.com/users/42
στο πρόγραμμα περιήγησης απευθείας;
Λάβετε υπόψη ότι αυτό το σφάλμα εμφανίζεται επίσης εάν ανανεώσετε το πρόγραμμα περιήγησης αφού έχετε ήδη επιτυχώς πλοηγηθεί σε αυτήν τη διεύθυνση URL μέσω του πλευρικού πλαισίου της εφαρμογής. Για να κατανοήσω αυτό το σφάλμα, επιτρέψτε μου να αναφερθώ τα επίσημα έγγραφα όπου είναι πραγματικά σαφές:
Μια δρομολογημένη εφαρμογή πρέπει να υποστηρίζει συνδέσμους σε βάθος. Ένας βαθύς σύνδεσμος είναι μια διεύθυνση URL που καθορίζει μια διαδρομή προς ένα στοιχείο εντός της εφαρμογής. Για παράδειγμα,
http://www.mysite.com/
είναι ένας βαθύς σύνδεσμος προς τη σελίδα λεπτομερειών του ήρωα που εμφανίζει τον ήρωα με αναγνωριστικό: 42.Δεν υπάρχει πρόβλημα όταν ο χρήστης πλοηγείται σε αυτήν τη διεύθυνση URL μέσα από έναν πελάτη που εκτελείται. Ο γωνιακός δρομολογητής ερμηνεύει τη διεύθυνση URL και τις διαδρομές προς αυτήν τη σελίδα και τον ήρωα.
Αλλά κάνοντας κλικ σε έναν σύνδεσμο σε ένα email, πληκτρολογώντας τον στη γραμμή διευθύνσεων του προγράμματος περιήγησης ή απλώς ανανεώνοντας το πρόγραμμα περιήγησης ενώ βρίσκεστε στη σελίδα λεπτομερειών του ήρωα - όλες αυτές οι ενέργειες αντιμετωπίζονται από το ίδιο το πρόγραμμα περιήγησης, εκτός της εφαρμογής που εκτελείται. Το πρόγραμμα περιήγησης υποβάλλει άμεσο αίτημα στον διακομιστή για τη συγκεκριμένη διεύθυνση URL, παρακάμπτοντας το δρομολογητή.Ένας στατικός διακομιστής επιστρέφει συνήθως το index.html όταν λαμβάνει αίτημα για
http://www.mysite.com/users/42
. Αλλά απορρίπτειsrc
και επιστρέφει ένα σφάλμα 404 - Not Found εκτός εάν έχει ρυθμιστεί να επιστρέφει index.html.
Για να επιλύσετε αυτό το ζήτημα είναι πολύ απλό, απλώς πρέπει να δημιουργήσουμε τη διαμόρφωση αρχείου του φορέα παροχής υπηρεσιών. Εφόσον δουλεύω με τις υπηρεσίες IIS εδώ, θα σας δείξω πώς να το κάνετε σε αυτό το περιβάλλον, αλλά η ιδέα είναι παρόμοια για το Apache ή για οποιονδήποτε άλλο διακομιστή ιστού.
Έτσι δημιουργούμε ένα αρχείο μέσα στο web.config
φάκελος που ονομάζεται angular-cli.json
μοιάζει με αυτό:
{ '$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': {} } }
Τότε πρέπει να είμαστε σίγουροι ότι αυτό το στοιχείο θα αντιγραφεί στον αναπτυσσόμενο φάκελο. Το μόνο που πρέπει να κάνουμε είναι να αλλάξουμε το αρχείο ρυθμίσεων Angular CLI AuthGuard
:
canActivateAuthGuard.ts
Θυμάστε πώς είχαμε το μάθημα helpers
εφαρμοστεί για να ορίσετε τη διαμόρφωση δρομολόγησης; Κάθε φορά που μεταβαίνουμε σε μια διαφορετική σελίδα, θα χρησιμοποιούμε αυτήν την τάξη για να επαληθεύσουμε εάν ο χρήστης έχει πιστοποιηθεί με ένα διακριτικό. Εάν όχι, θα ανακατευθυνθούμε αυτόματα στη σελίδα σύνδεσης. Το αρχείο για αυτό είναι 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; } }
—δημιουργήστε το μέσα στο canActivate
φάκελο και να έχει την εξής μορφή:
Router
Έτσι, κάθε φορά που αλλάζουμε τη σελίδα η μέθοδος Helper
θα κληθεί, το οποίο θα ελέγξει εάν ο χρήστης έχει πιστοποιηθεί και αν όχι, χρησιμοποιούμε το helpers
παράδειγμα για ανακατεύθυνση στη σελίδα σύνδεσης. Αλλά ποια είναι αυτή η νέα μέθοδος στο helpers.ts
τάξη? Κάτω από το localStorage
φάκελος ας δημιουργήσουμε ένα αρχείο localStorage
. Εδώ πρέπει να διαχειριστούμε sessionStorage
, όπου θα αποθηκεύσουμε το διακριτικό που λαμβάνουμε από το πίσω μέρος.
Σημείωση
Όσον αφορά το sessionStorage , μπορείτε επίσης να χρησιμοποιήσετε cookie ή localStorage και η απόφαση θα εξαρτηθεί από τη συμπεριφορά που θέλουμε να εφαρμόσουμε. Όπως υποδηλώνει το όνομα, sessionStorage είναι διαθέσιμο μόνο για τη διάρκεια της περιόδου λειτουργίας του προγράμματος περιήγησης και διαγράφεται όταν η καρτέλα ή το παράθυρο είναι κλειστό. Ωστόσο, επιβιώνει τις επαναφορτώσεις σελίδων. Εάν τα δεδομένα που αποθηκεύετε πρέπει να είναι διαθέσιμα σε συνεχή βάση, τότε localStorage είναι προτιμότερο από το 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()); } } . Τα cookie προορίζονται κυρίως για ανάγνωση από την πλευρά του διακομιστή, ενώ Subject μπορεί να διαβαστεί μόνο από την πλευρά του πελάτη. Το ερώτημα λοιπόν είναι, στην εφαρμογή σας, ποιος χρειάζεται αυτά τα δεδομένα --- τον πελάτη ή τον διακομιστή; |
{ path: 'logout', component: LogoutComponent},
Έχει νόημα ο κωδικός ελέγχου ταυτότητας τώρα; Θα επιστρέψουμε στο localStorage
τάξη αργότερα, αλλά τώρα ας επιστρέψουμε για ένα λεπτό στη διαμόρφωση δρομολόγησης. Ρίξτε μια ματιά σε αυτήν τη γραμμή:
components/login
Αυτό είναι το συστατικό μας για αποσύνδεση από τον ιστότοπο και είναι απλώς μια απλή τάξη για να καθαρίσετε το logout.component.ts
. Ας το δημιουργήσουμε κάτω από το 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']); } }
φάκελος με το όνομα /logout
:
localStorage
Έτσι, κάθε φορά που πηγαίνουμε στο URL 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']); }); } }
θα καταργηθεί και ο ιστότοπος θα ανακατευθύνει στη σελίδα σύνδεσης. Τέλος, ας δημιουργήσουμε app.component.ts
σαν αυτό:
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(); } }
Όπως μπορείτε να δείτε, προς το παρόν έχουμε κωδικοποιήσει τα διαπιστευτήριά μας εδώ. Σημειώστε ότι εδώ καλούμε μια τάξη υπηρεσιών. θα δημιουργήσουμε αυτές τις κατηγορίες υπηρεσιών για να αποκτήσουμε πρόσβαση στο πίσω μέρος μας στην επόμενη ενότητα.
Τέλος, πρέπει να επιστρέψουμε στο Subject
αρχείο, η διάταξη του ιστότοπου. Εδώ, εάν ο χρήστης έχει πιστοποιηθεί, θα εμφανίσει τις ενότητες μενού και κεφαλίδας, αλλά αν όχι, η διάταξη θα αλλάξει για να εμφανίζει μόνο τη σελίδα σύνδεσης.
Observable
Θυμηθείτε το Observable
τάξη στην τάξη βοηθών μας; Αυτό είναι ένα authentication
. app.component.html
s παρέχουν υποστήριξη για τη μετάδοση μηνυμάτων μεταξύ εκδοτών και συνδρομητών στην εφαρμογή σας. Κάθε φορά που αλλάζει το διακριτικό ελέγχου ταυτότητας, το Menu
η ιδιότητα θα ενημερωθεί. Επανεξέταση του services
αρχείο, πιθανότατα θα έχει πιο νόημα τώρα:
τι είναι ένα παιχνίδι blockchain
token.service.ts
Σε αυτό το σημείο μεταβαίνουμε σε διαφορετικές σελίδες, επικυρώνουμε την πλευρά των πελατών μας και αποδίδουμε μια πολύ απλή διάταξη. Αλλά πώς μπορούμε να λάβουμε δεδομένα από το πίσω μέρος; Συνιστώ ανεπιφύλακτα να κάνετε όλη την πρόσβαση back-end από υπηρεσία ειδικότερα τάξεις. Η πρώτη μας υπηρεσία θα βρίσκεται στο 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) ); } }
φάκελος, που ονομάζεται TokenService
:
BaseService
Η πρώτη κλήση στο πίσω μέρος είναι μια κλήση POST στο API διακριτικών. Το API διακριτικών δεν χρειάζεται τη συμβολοσειρά διακριτικών στην κεφαλίδα, αλλά τι θα συμβεί αν καλέσουμε άλλο τελικό σημείο; Όπως μπορείτε να δείτε εδώ, 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); } }
(και τάξεις υπηρεσιών γενικά) κληρονομούνται από το super.header
τάξη. Ας ρίξουμε μια ματιά σε αυτό:
localStorage
Έτσι, κάθε φορά που πραγματοποιούμε μια κλήση HTTP, εφαρμόζουμε την κεφαλίδα του αιτήματος χρησιμοποιώντας απλώς user.ts
. Εάν το διακριτικό είναι export class User { id: number; name: string; }
τότε θα προσαρτηθεί μέσα στην κεφαλίδα, αλλά αν όχι, θα ρυθμίσουμε τη μορφή JSON. Ένα άλλο πράγμα που μπορούμε να δούμε εδώ είναι τι συμβαίνει εάν ο έλεγχος ταυτότητας αποτύχει.
Το στοιχείο σύνδεσης θα καλέσει την κλάση υπηρεσίας και η κλάση υπηρεσίας θα καλέσει το πίσω μέρος. Μόλις αποκτήσουμε το διακριτικό, η τάξη βοηθού θα διαχειριστεί το διακριτικό και τώρα είμαστε έτοιμοι να λάβουμε τη λίστα χρηστών από τη βάση δεδομένων μας.
Για να λάβετε δεδομένα από τη βάση δεδομένων, πρώτα βεβαιωθείτε ότι ταιριάζουμε τις κατηγορίες μοντέλων με τα μοντέλα οπίσθιας όψης στην απόκρισή μας.
Σε 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)); }
Και μπορούμε να δημιουργήσουμε τώρα το SeedAPI.Web.API
αρχείο:
Web.API
Καλώς ήρθατε στο πρώτο βήμα της εφαρμογής Web API Core 2. Το πρώτο πράγμα που χρειαζόμαστε είναι να δημιουργήσουμε μια ASP.Net Core Web Application, την οποία θα ονομάσουμε ViewModels
.
Φροντίστε να επιλέξετε το Άδειο πρότυπο για καθαρή εκκίνηση, όπως μπορείτε να δείτε παρακάτω:
Αυτό είναι όλο, δημιουργούμε τη λύση ξεκινώντας από μια κενή εφαρμογή ιστού. Τώρα η αρχιτεκτονική μας θα είναι όπως παραθέτουμε παρακάτω, οπότε θα πρέπει να δημιουργήσουμε τα διαφορετικά έργα:
Για να το κάνετε αυτό, για καθένα απλώς κάντε δεξί κλικ στη Λύση και προσθέστε ένα έργο 'Βιβλιοθήκη κλάσης (.NET Core)'.
Στην προηγούμενη ενότητα δημιουργήσαμε οκτώ έργα, αλλά σε τι χρησιμεύουν; Ακολουθεί μια απλή περιγραφή του καθενός:
Interfaces
: Αυτό είναι το έργο εκκίνησής μας και όπου δημιουργούνται τελικά σημεία. Εδώ θα ρυθμίσουμε το JWT, τις εξαρτήσεις έγχυσης και τους ελεγκτές.Commons
: Εδώ πραγματοποιούμε μετατροπές από τον τύπο δεδομένων που οι ελεγκτές θα επιστρέψουν στις απαντήσεις στη διεπαφή. Είναι καλή πρακτική να ταιριάζετε αυτά τα μαθήματα με τα μοντέλα front-end.Models
: Αυτό θα είναι χρήσιμο στην εφαρμογή των εξαρτήσεων ένεσης. Το συναρπαστικό όφελος μιας στατικά δακτυλογραφημένης γλώσσας είναι ότι ο μεταγλωττιστής μπορεί να σας βοηθήσει να επαληθεύσετε ότι πληρούται πραγματικά ένα συμβόλαιο στο οποίο βασίζεται ο κώδικάς σας.ViewModels
: Όλες οι κοινόχρηστες συμπεριφορές και ο κωδικός χρησιμότητας θα είναι εδώ.Models
: Είναι καλή πρακτική να μην ταιριάζει απευθείας με τη βάση δεδομένων με την πρόσοψη Maps
, οπότε ο σκοπός της ViewModels
είναι η δημιουργία κλάσεων βάσεων δεδομένων οντοτήτων ανεξάρτητες από τη διεπαφή Αυτό θα μας επιτρέψει στο μέλλον να αλλάξουμε τη βάση δεδομένων μας χωρίς απαραίτητα να επηρεάσουμε τη διεπαφή μας. Βοηθά επίσης όταν απλά θέλουμε να κάνουμε κάποια αναδιαμόρφωση.Models
: Εδώ χαρτογραφούμε Services
έως Repositories
και αντίστροφα. Αυτό το βήμα καλείται μεταξύ ελεγκτών και υπηρεσιών.App_Start
: Μια βιβλιοθήκη για την αποθήκευση όλης της επιχειρηματικής λογικής.JwtTokenConfig.cs
: Αυτό είναι το μόνο μέρος όπου καλούμε τη βάση δεδομένων.Οι αναφορές θα έχουν την εξής μορφή:
Σε αυτήν την ενότητα, θα δούμε τη βασική διαμόρφωση του ελέγχου ταυτότητας διακριτικών και θα προχωρήσουμε λίγο πιο βαθιά στο θέμα της ασφάλειας.
Για να ξεκινήσετε να ρυθμίζετε το διακριτικό ιστού JSON (JWT) ας δημιουργήσουμε την επόμενη τάξη μέσα στο 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(); }); } } }
φάκελος που ονομάζεται appsettings.json
. Ο κώδικας στο εσωτερικό θα μοιάζει με αυτόν:
'Jwt': { 'Key': 'veryVerySecretKey', 'Issuer': 'http://localhost:50498/' }
Οι τιμές των παραμέτρων επικύρωσης θα εξαρτηθούν από την απαίτηση κάθε έργου. Ο έγκυρος χρήστης και το κοινό που μπορούμε να ρυθμίσουμε διαβάζοντας το αρχείο διαμόρφωσης ConfigureServices
:
startup.cs
Τότε πρέπει να το καλέσουμε μόνο από το // 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(); }
μέθοδος σε TokenController.cs
:
appsettings.json
Τώρα είμαστε έτοιμοι να δημιουργήσουμε τον πρώτο μας ελεγκτή που ονομάζεται 'veryVerySecretKey'
. Η τιμή που ορίσαμε στο LoginViewModel
έως ViewModels
πρέπει να ταιριάζει με αυτό που χρησιμοποιούμε για να δημιουργήσουμε το διακριτικό, αλλά πρώτα, ας δημιουργήσουμε το namespace SeedAPI.ViewModels { public class LoginViewModel : IBaseViewModel { public string username { get; set; } public string password { get; set; } } }
μέσα στο 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; } } }
έργο:
BuildToken
Και τέλος ο ελεγκτής:
Authenticate
Το identityDbContext
Η μέθοδος θα δημιουργήσει το διακριτικό με τον δεδομένο κωδικό ασφαλείας. Το Models
Η μέθοδος έχει μόνο την κωδικοποίηση χρήστη για την στιγμή, αλλά θα χρειαστεί να καλέσουμε τη βάση δεδομένων για να την επικυρώσουμε στο τέλος.
Η ρύθμιση του Entity Framework είναι πολύ εύκολη αφού η Microsoft κυκλοφόρησε την έκδοση Core 2.0— EF Core 2 εν συντομία. Θα πάμε σε βάθος με ένα μοντέλο με κωδικό-πρώτα χρησιμοποιώντας Context
, οπότε πρώτα βεβαιωθείτε ότι έχετε εγκαταστήσει όλες τις εξαρτήσεις. Μπορείτε να χρησιμοποιήσετε το NuGet για να το διαχειριστείτε:
Χρησιμοποιώντας το ApplicationContext.cs
έργο που μπορούμε να δημιουργήσουμε εδώ μέσα στο IApplicationContext.cs
φάκελο δύο αρχεία, EntityBase
και EntityBase
. Επίσης, θα χρειαζόμαστε ένα User.cs
τάξη.
Το IdentityUser
Τα αρχεία θα κληρονομηθούν από κάθε μοντέλο οντότητας, αλλά namespace SeedAPI.Models { public class User : IdentityUser { public string Name { get; set; } } }
είναι μια κλάση ταυτότητας και η μόνη οντότητα που θα κληρονομήσει από 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(); } } }
. Ακολουθούν και οι δύο τάξεις:
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(); } } } }
Τώρα είμαστε έτοιμοι να δημιουργήσουμε App_Start
, το οποίο θα μοιάζει με αυτό:
Web.API
Είμαστε πολύ κοντά, αλλά πρώτα θα χρειαστεί να δημιουργήσουμε περισσότερα μαθήματα, αυτή τη φορά στο 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' }); } } }
έργο. Η πρώτη τάξη είναι να προετοιμάσει το περιβάλλον εφαρμογής και η δεύτερη είναι να δημιουργήσει δείγματα δεδομένων μόνο για σκοπούς δοκιμής κατά τη διάρκεια της ανάπτυξης.
// 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
Και τους καλούμε από το αρχείο εκκίνησης:
DependencyInjectionConfig.cs
Είναι μια καλή πρακτική να χρησιμοποιείτε ένεση εξάρτησης για να μετακινηθείτε μεταξύ διαφορετικών έργων. Αυτό θα μας βοηθήσει να επικοινωνήσουμε μεταξύ ελεγκτών και χαρτογράφων, χαρτογράφων και υπηρεσιών και υπηρεσιών και αποθετηρίων.
Μέσα στο φάκελο namespace SeedAPI.Web.API.App_Start { public class DependencyInjectionConfig { public static void AddScope(IServiceCollection services) { services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); } } }
θα δημιουργήσουμε το αρχείο Map
και θα μοιάζει με αυτό:
Service
Θα χρειαστεί να δημιουργήσουμε για κάθε νέα οντότητα ένα νέο Repository
, startup.cs
και // 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(); }
και να ταιριάξουμε με αυτό το αρχείο. Τότε πρέπει απλώς να το καλέσουμε από το 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) { } } }
αρχείο:
Authorize
Τέλος, όταν πρέπει να λάβουμε τη λίστα χρηστών από τη βάση δεδομένων, μπορούμε να δημιουργήσουμε έναν ελεγκτή χρησιμοποιώντας αυτήν την ένεση εξάρτησης:
Map
Δείτε πώς το Maps
υπάρχει ένα χαρακτηριστικό εδώ για να είστε σίγουροι ότι το μπροστινό άκρο έχει συνδεθεί και πώς λειτουργεί η έγχυση εξάρτησης στον κατασκευαστή της κλάσης.
Έχουμε τελικά μια κλήση στη βάση δεδομένων, αλλά πρώτα, πρέπει να κατανοήσουμε το ViewModels
έργο.
UserMap.cs
ΕργοΑυτό το βήμα είναι απλώς στο χάρτη 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; } } }
από και προς μοντέλα βάσεων δεδομένων. Πρέπει να δημιουργήσουμε ένα για κάθε οντότητα και, ακολουθώντας το προηγούμενο, το παράδειγμα Services
το αρχείο θα μοιάζει με αυτό:
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(); } } }
Φαίνεται για άλλη μια φορά, η εξάρτηση εξάρτησης λειτουργεί στον κατασκευαστή της τάξης, συνδέοντας τους Χάρτες με το έργο Υπηρεσίες.
Repositories
ΕργοΔεν υπάρχουν πολλά να πούμε εδώ: Το παράδειγμά μας είναι πραγματικά απλό και δεν έχουμε επιχειρηματική λογική ή κώδικα για να γράψουμε εδώ. Αυτό το έργο θα αποδειχθεί χρήσιμο σε μελλοντικές προηγμένες απαιτήσεις όταν πρέπει να υπολογίσουμε ή να κάνουμε κάποια λογική πριν ή μετά τα βήματα της βάσης δεδομένων ή του ελεγκτή. Ακολουθώντας το παράδειγμα, η τάξη θα φαίνεται αρκετά γυμνή:
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; } } } }
ΕργοΦτάνουμε στην τελευταία ενότητα αυτού του σεμιναρίου: Απλώς πρέπει να πραγματοποιούμε κλήσεις στη βάση δεδομένων, επομένως δημιουργούμε ένα
|_+_|αρχείο όπου μπορούμε να διαβάσουμε, να εισαγάγουμε ή να ενημερώσουμε τους χρήστες στη βάση δεδομένων.
|_+_|
Σε αυτό το άρθρο, εξήγησα πώς να δημιουργήσετε μια καλή αρχιτεκτονική χρησιμοποιώντας το Angular 5 και το Web API Core 2. Σε αυτό το σημείο, έχετε δημιουργήσει τη βάση για ένα μεγάλο έργο με κώδικα που υποστηρίζει μια μεγάλη αύξηση των απαιτήσεων.
Η αλήθεια είναι ότι τίποτα δεν ανταγωνίζεται τη JavaScript στο μπροστινό μέρος και τι μπορεί να ανταγωνιστεί το C # εάν χρειάζεστε την υποστήριξη του SQL Server και του Entity Framework στο πίσω μέρος; Έτσι, η ιδέα αυτού του άρθρου ήταν να συνδυάσει τα καλύτερα από δύο κόσμους και ελπίζω να το απολαύσατε.
Εάν εργάζεστε σε μια ομάδα Γωνιακοί προγραμματιστές μάλλον θα μπορούσαν να υπάρχουν διαφορετικοί προγραμματιστές που εργάζονται στο μπροστινό και στο πίσω μέρος, οπότε μια καλή ιδέα να συγχρονίσετε τις προσπάθειες και των δύο ομάδων θα μπορούσε να είναι η ενσωμάτωση του Swagger με το Web API 2. Το Swagger είναι ένα εξαιρετικό εργαλείο για την τεκμηρίωση και τη δοκιμή των API RESTFul. Διαβάστε τον οδηγό της Microsoft: Ξεκινήστε με το Swashbuckle και το ASP.NET Core .
Εάν εξακολουθείτε να είστε πολύ νέοι στο Angular 5 και αντιμετωπίζετε προβλήματα με την παρακολούθηση, διαβάστε Ένα Angular 5 Tutorial: Βήμα προς βήμα Οδηγός για την πρώτη σας εφαρμογή Angular 5 από τους συναδέλφους ApeeScapeer Sergey Moiseev.
Μπορείτε να χρησιμοποιήσετε είτε IDE για το frontend, πολλοί άνθρωποι επιθυμούν να ενταχθούν στο frontend σε μια Βιβλιοθήκη Εφαρμογών Ιστού και να αυτοματοποιήσουν την ανάπτυξη. Προτιμώ να διατηρήσω τον κώδικα front-end διαχωρισμένο από το back-end και βρήκα το Visual Studio Code να είναι ένα πολύ καλό εργαλείο, ειδικά με intellisense, για τον κώδικα Typescript.
Το Angular Material είναι ένα πλαίσιο συνιστωσών διεπαφής χρήστη και ενώ δεν απαιτείται να το χρησιμοποιήσετε. Τα πλαίσια UI Component μας βοηθούν να οργανώσουμε τη διάταξη και την απόκριση στον ιστότοπο, αλλά έχουμε πολλά από αυτά στην αγορά, όπως bootstrap και άλλα και μπορούμε να επιλέξουμε την εμφάνιση και την αίσθηση που προτιμούμε.
Ο γωνιακός δρομολογητής επιτρέπει την πλοήγηση από τη μία προβολή στην άλλη καθώς οι χρήστες εκτελούν εργασίες εφαρμογής. Μπορεί να ερμηνεύσει μια διεύθυνση URL του προγράμματος περιήγησης ως οδηγία για την πλοήγηση σε μια προβολή που δημιουργείται από πελάτη. Επιτρέπεται στον προγραμματιστή να ρυθμίσει ονόματα URL, παραμέτρους και μολυσμένο με CanAuthenticate, μπορούμε να επικυρώσουμε την ταυτότητα χρήστη
Συχνά τα μαθήματα χρειάζονται πρόσβαση το ένα στο άλλο, και αυτό το μοτίβο σχεδίασης δείχνει πώς να δημιουργείτε χαλαρά συνδεδεμένες τάξεις. Όταν δύο τάξεις συνδέονται στενά, συνδέονται με δυαδικό συσχετισμό.
Το JSON Web Token (JWT) είναι μια συμπαγής βιβλιοθήκη για την ασφαλή μετάδοση πληροφοριών μεταξύ των μερών ως αντικείμενο JSON. Τα JWT μπορούν να κρυπτογραφηθούν για να παρέχουν επίσης μυστικότητα μεταξύ των μερών, θα επικεντρωθούμε σε υπογεγραμμένα διακριτικά.