利用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...

相关文章
相关标签/搜索