利用angular4和nodejs-express構建簡單網站(十一)—HttpClient攔截器和路由守衛

上一節介紹了好友模塊,這一節介紹和好友模塊中的控件有關的三個服務程序。前端

用HttpClient攔截器發送用戶認證信息

在進入好友模塊以前,須要向服務器發送認證信息,在這裏使用angular的HttpClient攔截器進行發送。
攔截器的官方解釋爲:HTTP 攔截機制是 @angular/common/http 中的主要特性之一。 使用這種攔截機制,你能夠聲明一些攔截器,用它們監視和轉換從應用發送到服務器的 HTTP 請求。 攔截器還能夠用監視和轉換從服務器返回到本應用的那些響應。 多個選擇器會構成一個「請求/響應處理器」的雙向鏈表。若是想詳細瞭解攔截器,能夠看官方文檔
咱們利用攔截器在每次向服務器請求朋友列表時將認證信息加入到頭部。
具體代碼以下:git

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/observable';
import { AuthTokenService } from './authtoken.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor{
    constructor(
        private tokenServ: AuthTokenService
    ){}
    intercept(req: HttpRequest<any>,next: HttpHandler) : Observable<HttpEvent<any>>{
        //獲取認證信息
         const auth = this.tokenServ.getToken();
        //克隆request,加入新的頭信息
        const authReq = req.clone({headers:req.headers.set('Authorization', 'Bearer ' + auth)});
        return next.handle(authReq);
    }
}

要實現攔截器,就要實現一個實現了 HttpInterceptor 接口中的 intercept() 方法的類(AuthInterceptor)。在intercept()方法中先經過AuthTokenService的getToken()方法取得認證信息。這些認證信息是在登陸或註冊成功後由服務器發回來的jwt認證信息。服務器如何發送這些信息請參考第三節的內容,認證信息的內容是登陸或認證的用戶ID。由於HttpRequest 實例的屬性倒是隻讀(readonly)的,要修改請求信息只能先克隆它。在這裏利用clone()方法在請求的頭部信息中加入認證信息( clone() 方法的哈希型參數容許你在複製出克隆體的同時改變該請求的某些特定屬性)。最後調用 next.handle(),以便這個請求流能走到下一個攔截器,並最終傳給後端處理器。
最後還須要向模塊這個攔截器,這個AuthInterceptor攔截器就是一個由 Angular 依賴注入 (DI)系統管理的服務,你必須在提供 HttpClient 的同一個(或其各級父注入器)注入器中提供這些攔截器。在好友模塊的providers中加入github

{
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi:true
  }

如今在好友模塊中每一個發送到服務器的請求都會在頭部加上認證信息。
補充內容:服務器jwt認證中間件
express的jwt中間件定義代碼以下:express

var expressJwt = require('express-jwt');
//使用jwt攔截
app.use(expressJwt({
  secret: 'secret'
}));
//處置jwt異常
app.use(function (err, req, res, next) {
  if (err.name === 'UnauthorizedError') {
    res.status(401).send({
      'code': 401,
      'msg': 'invalid token'
    });
  }
});
app.use('/friends', friends);

必定要把處理friends訪問的路由放到jwt中間件後面,否則jwt沒法進行驗證。segmentfault

利用路由守衛保證未登陸用戶沒法訪問好友信息

在上一節介紹路由時,在路由配置中加入了canActivate: [AuthGuardService],這是angular路由守衛服務,路由守衛的做用在官方文檔中的解釋以下:
如今,任何用戶都能在任什麼時候候導航到任何地方。 但有時候這樣是不對的。
該用戶可能無權導航到目標組件。
可能用戶得先登陸(認證)。
在顯示目標組件前,你可能得先獲取某些數據。
在離開組件前,你可能要先保存修改。
你可能要詢問用戶:你是否要放棄本次更改,而不用保存它們?
你能夠往路由配置中添加守衛,來處理這些場景。後端

守衛返回一個值,以控制路由器的行爲:
若是它返回 true,導航過程會繼續
若是它返回 false,導航過程會終止,且用戶會留在原地。
在這裏咱們利用路由守衛要求用戶先登陸才能導航到birthday模塊中的控件。
代碼以下:服務器

import { Injectable } from '@angular/core';
import {
    CanActivate,
    ActivatedRouteSnapshot,
    RouterStateSnapshot,
    Router
} from '@angular/router';
import { UserService } from './user.service';
import { AuthTokenService } from './authtoken.service';
@Injectable()
export class AuthGuardService implements CanActivate {
    constructor(
        private tokenServe: AuthTokenService,
        private router: Router) { }
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (this.tokenServe.getToken() !== null) {
            return true;
        }
        this.router.navigate(['/login']);
        return false;
    }
}

路由守衛類也是一個注入服務類,它須要實現CanActivate接口的canActivate()方法。canActivate()方法實現了守衛代碼。代碼很簡單,從AuthTokenService類的getToken()中獲取認證信息的值,若是有就返回true,若是沒有就導航到登陸頁面。並返回false。
最後記住在birthday模塊中providers中加入AuthGuardService。app

birthday.service數據提供服務介紹

BirthdayService類爲birthday模塊提供了數據服務,代碼以下:框架

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders,HttpErrorResponse } from '@angular/common/http';
import { UserService } from '../user.service';

import 'rxjs/add/operator/map';

export class Friend {
    constructor(
        public fid: number,
        public fname: string,
        public fbirth: Date,
        public fnumber: string,
        public femail: string,
        public fgroup: string,
        public state: string,
        public photo: string,
        public uid:number
    ) { }
}

@Injectable()
export class BirthdayService {
    constructor(
        private userServ: UserService,
        private http: HttpClient) {
    }
    //獲取所有朋友信息
    getFriends() {
        return this.http.get('http://localhost:3000/friends/friend-list',
            { observe: 'response'});}
    //獲取單個朋友信息
    getFriend(id: number | string) {
        return this.getFriends().map(res => {
            if (res.body['code'] === '200') {
                return res.body['results'].find(result => result.fid === +id);}
        });
    }
    //修改朋友信息
    editFriend(friend: Friend){
        const body = {'value':friend,'operate':'edit'};
        return this.http.post('http://localhost:3000/friends/editfriend',body);
    }
    //新建朋友信息
    newFriend(friend: Friend){
        const body = {'value':friend,'operate':'new'};
        return this.http.post('http://localhost:3000/friends/editfriend', body);
    }
    //刪除好友
    deleteFriend(friend:Friend){
        const body = {'value':friend, 'operate':'delete'};
        return this.http.post('http://localhost:3000/friends/editfriend',body);
    }
    //錯誤處理
    handleError(err: HttpErrorResponse): string {
        if (err.error instanceof Error) {
            return '發生錯誤,錯誤信息:' + err.error.message;
        } else {
            console.log(`Backend returned code ${err.status}, body was: ${err.error['msg']}`);
            return err.error['msg'];
        }
    }
}

首先在類外定義了一個Friend類,在這個類中定義了friend信息。BirthdayService類的主要功能有6部分:ide

  • 獲取所有朋友信息。經過HttpClient的get方法發送獲取到所有的friend信息的請求。
  • 獲取單個朋友信息。getFriends()方法返回的是一個Observable對象,利用Observable的map()函數的回調找到對應id的單個friend對象,並繼續發射Observable對象。
  • 修改朋友信息。將修改後的friend信息post到服務器。在發送的body中,除了修改後的friend對象,還發送了一個字符串屬性:'operate':'edit',用於區分是修改friend仍是新建friend,這了的edit表明修改信息。(具體的服務器操做代碼將在下一章介紹)。
  • 新建朋友信息。和修改friend信息同理,只不過將body中的'operate'改成'new'。
  • 刪除好友。也和修改friend信息同理,只不過將body中的'operate'改成'delete'。
  • 錯誤處理。若是是客戶端(angular代碼)出了錯,會拋出一個 Error 類型的異常,由此判斷若是錯誤的類型是Error類型,就表示前端出錯,返回一條錯誤信息:'發生錯誤,錯誤信息:' + err.error.message;。若是是後端出錯,就打印出錯誤狀態和信息。

關於birthday模塊的服務程序就介紹完了。下一章將要介紹服務器端express框架如何處理這些請求。今天將個人代碼傳到了github上,方便你們參考。地址以下:
前端:https://github.com/db991400/b...
後端:https://github.com/db991400/b...

相關文章
相關標籤/搜索