Skip to content

Latest commit

 

History

History
437 lines (388 loc) · 11.4 KB

问道Angular——实现Angular功能齐全的拦截器.md

File metadata and controls

437 lines (388 loc) · 11.4 KB

utils.ts

import { environment } from '@env/environment';
const ACCESS_TOKEN = environment.cookie_name || 'access_token';
const DOMAIN_NAME = environment.domain_name;
const EXPIRE_TIME = environment.expire_time || 30;

export const PrefixDate = (time) => {
  time = Number(time);
  return time < 10 ? '0' + time : time;
};

export const FormatDate = (dateTime, format = 'YYYY-MM-DD HH:mm') => {
  dateTime = isNaN(Number(dateTime)) ? dateTime : Number(dateTime);

  if (typeof dateTime === 'string') {
    dateTime = dateTime.replace(/\-/g, '/');
    dateTime = new Date(dateTime);
  } else if (typeof dateTime === 'number') {
    dateTime = new Date(dateTime);
  } else if (!(dateTime instanceof Date)) {
    dateTime = new Date();
  }

  const week = ['日', '一', '二', '三', '四', '五', '六'];
  return format.replace(/YYYY|YY|MM|DD|HH|hh|mm|SS|ss|week/g, function(key) {
    switch (key) {
      case 'YYYY':
        return dateTime.getFullYear();
      case 'YY':
        return(dateTime.getFullYear() + '').slice(2);
      case 'MM':
        return PrefixDate(dateTime.getMonth() + 1);
      case 'DD':
        return PrefixDate(dateTime.getDate());
      case 'HH':
      case 'hh':
        return PrefixDate(dateTime.getHours());
      case 'mm':
        return PrefixDate(dateTime.getMinutes());
      case 'SS':
      case 'ss':
        return PrefixDate(dateTime.getSeconds());
      case 'week':
        return week[dateTime.getDay()];
    }
  });
};


// 设置cookie, expiredays有效天数
export const setCookie = (value, key = ACCESS_TOKEN, expiredays = EXPIRE_TIME) => {
  value = encodeURIComponent(value);
  const exdate = new Date();
  exdate.setDate(exdate.getDate() + expiredays);
  const exdatestr = exdate.toUTCString();
  document.cookie = typeof DOMAIN_NAME !== 'undefined' ?
    `${key}=${value};expires=${exdatestr};path=/;domain=${DOMAIN_NAME}` :
    `${key}=${value};expires=${exdatestr};path=/;`;
};

export const getCookie = (key = ACCESS_TOKEN) => {
  const reg = new RegExp(`(^| )${key}=([^;]*)(;|$)`);
  const arr = document.cookie.match(reg);
  return arr ? decodeURIComponent(arr[2]) : null;
};

// 清除cookie
export const removeCookie = (key = ACCESS_TOKEN) => {
  setCookie('', key, -1);
};

export const getStorage = (key = ACCESS_TOKEN, day = EXPIRE_TIME) => {
  const dateStr = localStorage.getItem(key);
  if (!dateStr) { return null; }
  const obj = JSON.parse(dateStr);
  if (new Date().getTime() - Number(obj.date) > 86400000 * day) {
    return null;
  }
  return obj.value;
};

export const setStorage = (value, key = ACCESS_TOKEN) => {
  const params = {
    date: new Date().getTime(),
    value
  };
  localStorage.setItem(key, JSON.stringify(params));
};

export const removeStorage = (key = ACCESS_TOKEN) => {
  localStorage.removeItem(key);
};

export const clearStorage = () => {
  localStorage.clear();
};

alert.service.ts

import { Injectable } from '@angular/core';
import {NzModalService} from 'ng-zorro-antd';

@Injectable({
  providedIn: 'root'
})
export class AlertService {

  private modalRef = [];

  constructor(private modalService: NzModalService) { }

  // 弹窗
  alert(type: string, params: object, content?: string, title?: string): void {
    params = this.getAlertParams(params, content, title);
    this.modalRef.push(this.modalService[type](params));
  }

  closeAlert() {
    if (this.modalRef.length > 0) {
      this.modalRef.pop().close();
    }
  }

  closeAllAlert() {
    this.modalRef = [];
    this.modalService.closeAll();
  }

  // 确认框
  confirm(content, cb) {
    this.modalRef.push(this.modalService.confirm({
      nzContent: content,
      nzTitle: '',
      nzIconType: '',
      nzOnCancel: () => this.closeAlert(),
      nzOnOk: () => {
        this.closeAlert();
        cb();
      }
    }));
  }

  getAlertParams(params: object, content?: string, title?: string): object {
    if (!!params) {
      params['nzOnOk'] = () => this.closeAlert();
      params['nzOkText'] = '确定';
      return params;
    }
    params = {};
    if (content) { params['nzContent'] = content; }
    if (title) {
      params['nzTitle'] = title;
    } else {
      params['nzIconType'] = '';
    }
    params['nzOkText'] = '确定';
    params['nzOnOk'] = () => this.closeAlert();
    return params;
  }
}

httpService.ts

import {Injectable} from '@angular/core';
import {HttpClient, HttpParams} from '@angular/common/http';
import {Observable} from 'rxjs/index';
import {LoadingService} from '@services/loading.service';
import {environment} from '@env/environment';
import {getCookie, removeCookie} from '@core/utils';

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor(private http: HttpClient,
              private loadingService: LoadingService) {
  }

  // 获取配置信息
  getConfig(): Object {
    return environment;
  }

  setLoading(flag: boolean): void {
    this.loadingService.setLoading(flag);
  }

  // get请求
  get(url: string, params: HttpParams | object = null, isLoading: boolean = true): Observable<Object> {
    if (isLoading) {
      this.loadingService.setLoading(true);
    }
    if (!!params) {
      return this.http.get(url, {params: params as HttpParams | { [param: string]: string | string[]; }});
    }
    return this.http.get(url);
  }

  // post请求
  post(url: string, body?: any, params: HttpParams | object = null, isLoading: boolean = true): Observable<Object> {
    if (isLoading) {
      this.loadingService.setLoading(true);
    }
    if (!!params) {
      this.http.post(url, body, {params: params as HttpParams | { [param: string]: string | string[]; }});
    }
    return this.http.post(url, body);
  }

  // delete请求
  delete(url: string, params: HttpParams | object = null, isLoading: boolean = true): Observable<Object> {
    if (isLoading) {
      this.loadingService.setLoading(true);
    }
    if (!!params) {
      return this.http.delete(url, {params: params as HttpParams | { [param: string]: string | string[]; }});
    }
    return this.http.delete(url);
  }

  // patch请求
  patch(url: string, body?: any, params: HttpParams | object = null, isLoading: boolean = true): Observable<Object> {
    if (isLoading) {
      this.loadingService.setLoading(true);
    }
    if (!!params) {
      this.http.patch(url, body, {params: params as HttpParams | { [param: string]: string | string[]; }});
    }
    return this.http.patch(url, body);
  }

  // 退出登录
  logout() {
    removeCookie();
    window.location.href = environment.locationUrl;
  }

  // 判断是否已经登录
  isLoggedIn() {
    return !!getCookie();
  }

}

message.service.ts

import { Injectable } from '@angular/core';
import {NzMessageService} from 'ng-zorro-antd';

@Injectable({
  providedIn: 'root'
})
export class MessageService {

  messageId = null;

  constructor(private messageService: NzMessageService) { }

  message(type: string, content: string) {
    this.messageId = this.messageService[type](content);
  }
}

londing.service.ts

import { Injectable } from '@angular/core';
import {Observable, Subject, timer} from 'rxjs/index';

@Injectable({
  providedIn: 'root'
})
export class LoadingService {

  private loadingSubject = new Subject<boolean>();
  isLoading: Observable<boolean> = this.loadingSubject.asObservable();

  constructor() { }

  setLoading(flag: boolean) {
    setTimeout(() => {
      this.loadingSubject.next(flag);
    });
  }
}

token.interceptor.ts

import {Injectable} from '@angular/core';
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable, throwError} from 'rxjs/index';
import {HttpService} from '@services/http.service';
import {getCookie} from '@core/utils';
import {LoadingService} from '@services/loading.service';
import {catchError, delay, retryWhen, scan, tap, timeout} from 'rxjs/internal/operators';
import {MessageService} from '@services/message.service';
import {Router} from '@angular/router';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private environment: object;
  private readonly DEFAULT_TIME_OUT = 1000;
  private readonly REPLY_TIME = 3;

  constructor(private httpService: HttpService,
              private loadingService: LoadingService,
              private router: Router,
              private messageService: MessageService) {
    this.environment = this.httpService.getConfig();
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(
      this.cloneHeader(req)
    ).pipe(
      timeout(this.DEFAULT_TIME_OUT),
      retryWhen(error => {
        return error.pipe(
          scan((count, err) => {
            if (count < this.REPLY_TIME) {
              return count += 1;
            }
            throw err;
          }, 0),
          delay(500),
          tap(count => {
            if (+count === 1) {
              console.log('连接超时,正在重新请求……');
            }
          })
        );
      }),
      catchError(error => this.handleError(error))
    );
  }

  private prepareUrl(url: string): string {
    url = /^https?:\/\//i.test(url) ? url : this.environment['apiUrl'] + '/' + url;
    return url.replace(/([^:]\/)\/+/g, '$1');
  }

  cloneHeader(req: HttpRequest<any>): HttpRequest<any> {
    const token: string = getCookie();
    const url: string = this.prepareUrl(req.url);
    if (token && token.length > 0) {
      return req.clone({
        headers: req.headers.set('Authorization', 'Bearer ' + token),
        url
      });
    }
    return req.clone({
      url
    });
  }

  private handleError(error: Error | HttpErrorResponse) {
    this.loadingService.setLoading(false);
    if (error instanceof HttpErrorResponse) {
      if (!navigator.onLine) {
        this.messageService.message('error', '网络断开');
      } else {
        switch (error.error.status_code) {
          case 401:
            // token过期,删除本地cookie,跳回登录页面
            this.httpService.logout();
            break;
          case 404:
            this.router.navigate(['404']);
            break;
          case 405:
          case 417:
          case 422:
            // 后台返回错误信息,直接弹窗提示
            this.messageService.message('error', error.error.message);
            break;
          case 500:
            this.router.navigate(['500']);
            break;
        }
      }
    } else {
      this.messageService.message('error', '客户端错误');
    }
    return throwError(error);
  }
}

loading调用

isLoading$: Observable<boolean>;

  constructor(private httpService: HttpService,
              private loadingService: LoadingService) {
    this.isLoading$ = this.loadingService.isLoading;
  }

loading的html

<div class="my-wrapper">
  <div class="loading-wrapper">
    <ng-template #indicatorTemplate><div class="loading-box"></div></ng-template>
    <nz-spin [nzIndicator]="indicatorTemplate" [nzSpinning]="isLoading$ | async" [nzDelay]="500"></nz-spin>
  </div>
  <router-outlet></router-outlet>
</div>

loading的css

.my-wrapper {
  height: 100%;
  overflow-y: auto;
  position: relative;
}

.loading-wrapper {
  height: 0;
}

.loading-box {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background:rgba(255,255,255,.5) url("../assets/images/loading.gif") center center no-repeat ;
  background-size: 160px;
  z-index: 99999;
}

app.module.ts

providers: [
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
  ],