1. 程式人生 > >angular HTTP攔截器

angular HTTP攔截器

當後臺使用c#的abp框架時,而前端不使用abp帶的angular模版,比如ionic專案,專案介面還是使用swagger生成,這是我們還是需要在請求頭上帶有token、tenantId資訊、包括一些錯誤處理,可以仿造abp自帶模版自己寫個http攔截器。

import {Observable, of, Subject} from 'rxjs';
import {MessageService} from './message.service';
import {HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse, HttpHeaders} from '@angular/common/http';
import {Injectable, Injector} from '@angular/core';

export interface IValidationErrorInfo {
message: string;
members: string[];
}

export interface IErrorInfo {
code: number;
message: string;
details: string;
validationErrors: IValidationErrorInfo[];
}

export interface IAjaxResponse {
success: boolean;
result?: any;
targetUrl?: string;
error?: any;
unAuthorizedRequest: boolean;
__abp: boolean;
}

@Injectable()
export class AbpHttpConfiguration {
constructor(private _messageService: MessageService) {
}

defaultError = {
    message: '產生了一個錯誤!',
    details: '伺服器沒有傳送錯誤詳情.'
};
defaultError401 = {
    message: '您未經過身份驗證!',
    details: '為了執行此操作,您應該進行身份驗證(登入).'
};
defaultError403 = {
    message: '您沒有許可權!',
    details: '您不能執行此操作.'
};
defaultError404 = {
    message: '未找到相關資源!',
    details: '伺服器上找不到請求的資源.'
};

logError(error): void {
    console.log(error);
}

showError(error): any {
    if (error.details) {
        return this._messageService.error(error.details, error.message || this.defaultError.message);
    } else {
        return this._messageService.error(error.message || this.defaultError.message);
    }
}

handleTargetUrl(targetUrl: string): void {
    if (!targetUrl) {
        location.href = '/';
    } else {
        location.href = targetUrl;
    }
}

handleUnAuthorizedRequest(messagePromise: any, targetUrl?: string): void {
    const _this = this;
    const self = this;
    if (messagePromise) {
        messagePromise.done(function () {
            _this.handleTargetUrl(targetUrl || '/');
        });
    } else {
        self.handleTargetUrl(targetUrl || '/');
    }
}

handleNonAbpErrorResponse(response: HttpResponse<any>): void {
    const self = this;
    switch (response.status) {
        case 401:
            self.handleUnAuthorizedRequest(self.showError(self.defaultError401), '/');
            break;
        case 403:
            self.showError(self.defaultError403);
            break;
        case 404:
            self.showError(self.defaultError404);
            break;
        default:
            self.showError(self.defaultError);
            break;
    }
}

handleAbpResponse(response: HttpResponse<any>, ajaxResponse: IAjaxResponse): HttpResponse<any> {
    let newResponse;
    if (ajaxResponse.success) {
        newResponse = response.clone({
            body: ajaxResponse.result
        });
        if (ajaxResponse.targetUrl) {
            this.handleTargetUrl(ajaxResponse.targetUrl);
        }
    } else {
        newResponse = response.clone({
            body: ajaxResponse.result
        });
        if (!ajaxResponse.error) {
            ajaxResponse.error = this.defaultError;
        }
        this.logError(ajaxResponse.error);
        this.showError(ajaxResponse.error);
        if (response.status === 401) {
            this.handleUnAuthorizedRequest(null, ajaxResponse.targetUrl);
        }
    }
    return newResponse;
}

getAbpAjaxResponseOrNull(response: HttpResponse<any>): IAjaxResponse | null {
    if (!response || !response.headers) {
        return null;
    }
    const contentType = response.headers.get('Content-Type');
    if (!contentType) {
        // this._logService.warn('Content-Type is not sent!');
        console.warn('Content-Type is not sent!');
        return null;
    }
    if (contentType.indexOf('application/json') < 0) {
        // this._logService.warn('Content-Type is not application/json: ' + contentType);
        console.log('Content-Type is not application/json: ' + contentType);
        return null;
    }
    const responseObj = JSON.parse(JSON.stringify(response.body));
    if (!responseObj.__abp) {
        return null;
    }
    return responseObj;
}

handleResponse(response: HttpResponse<any>): HttpResponse<any> {
    const ajaxResponse = this.getAbpAjaxResponseOrNull(response);
    if (ajaxResponse == null) {
        return response;
    }
    return this.handleAbpResponse(response, ajaxResponse);
}

blobToText(blob: any): Observable<string> {
    return new Observable(function (observer) {
        if (!blob) {
            observer.next('');
            observer.complete();
        } else {
            const reader = new FileReader();
            reader.onload = function () {
                observer.next(this.result);
                observer.complete();
            };
            reader.readAsText(blob);
        }
    });
}

}

@Injectable()
export class AbpHttpInterceptor implements HttpInterceptor {
protected configuration: AbpHttpConfiguration;
// private _tokenService;
// private _utilsService;
constructor(private injector: Injector) {
this.configuration = this.injector.get(AbpHttpConfiguration);
}

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const _this = this;
    const interceptObservable: Subject<HttpEvent<any>> = new Subject();
    const modifiedRequest = this.normalizeRequestHeaders(request);
    next.handle(modifiedRequest)
        .subscribe(function (event) {
            _this.handleSuccessResponse(event, interceptObservable);
        }, function (error) {
            return _this.handleErrorResponse(error, interceptObservable);
        });
    return interceptObservable;
}

protected normalizeRequestHeaders(request: HttpRequest<any>): HttpRequest<any> {
    let modifiedHeaders = new HttpHeaders();
    modifiedHeaders = request.headers.set('Pragma', 'no-cache')
        .set('Cache-Control', 'no-cache')
        .set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT');
    modifiedHeaders = this.addXRequestedWithHeader(modifiedHeaders);
    modifiedHeaders = this.addAuthorizationHeaders(modifiedHeaders);
    modifiedHeaders = this.addAspNetCoreCultureHeader(modifiedHeaders);
    modifiedHeaders = this.addAcceptLanguageHeader(modifiedHeaders);
    modifiedHeaders = this.addTenantIdHeader(modifiedHeaders);
    return request.clone({
        headers: modifiedHeaders
    });
}

protected addXRequestedWithHeader(headers: HttpHeaders): HttpHeaders {
    if (headers) {
        headers = headers.set('X-Requested-With', 'XMLHttpRequest');
    }
    return headers;
}

protected addAspNetCoreCultureHeader(headers: HttpHeaders): HttpHeaders {
    // todo: 如果需要再新增.AspNetCore.Culture
    /*const cookieLangValue = this._utilsService.getCookieValue('Abp.Localization.CultureName');
    if (cookieLangValue && headers && !headers.has('.AspNetCore.Culture')) {
        headers = headers.set('.AspNetCore.Culture', cookieLangValue);
    }*/
    return headers;
}

protected addAcceptLanguageHeader(headers: HttpHeaders): HttpHeaders {
    // todo: 如果需要再新增Accept-Language
    /*const cookieLangValue = this._utilsService.getCookieValue('Abp.Localization.CultureName');
    if (cookieLangValue && headers && !headers.has('Accept-Language')) {
        headers = headers.set('Accept-Language', cookieLangValue);
    }*/
    return headers;
}

protected addTenantIdHeader(headers: HttpHeaders): HttpHeaders {
    // todo: 如果需要再新增Abp.TenantId
    // const cookieTenantIdValue = this._utilsService.getCookieValue('Abp.TenantId');
    // if (cookieTenantIdValue && headers && !headers.has('Abp.TenantId')) {
    //     headers = headers.set('Abp.TenantId', cookieTenantIdValue);
    // }
    return headers;
}

protected addAuthorizationHeaders(headers: HttpHeaders): HttpHeaders {
    let authorizationHeaders = headers ? headers.getAll('Authorization') : null;
    if (!authorizationHeaders) {
        authorizationHeaders = [];
    }
    if (!this.itemExists(authorizationHeaders, function (item) {
            return item.indexOf('Bearer ') === 0;
        })) {
        /*const token = this._tokenService.getToken();
        if (headers && token) {
            headers = headers.set('Authorization', 'Bearer ' + token);
        }*/
    }
    return headers;
}

protected handleSuccessResponse(event: HttpEvent<any>, interceptObservable: Subject<HttpEvent<any>>): void {
    const self = this;
    if (event instanceof HttpResponse) {
        if (event.body instanceof Blob && event.body.type && event.body.type.indexOf('application/json') >= 0) {
            const clonedResponse = event.clone();
            self.configuration.blobToText(event.body).subscribe(function (json) {
                const responseBody = json === 'null' ? {} : JSON.parse(json);
                const modifiedResponse = self.configuration.handleResponse(event.clone({
                    body: responseBody
                }));
                interceptObservable.next(modifiedResponse.clone({
                    body: new Blob([JSON.stringify(modifiedResponse.body)], {type: 'application/json'})
                }));
                interceptObservable.complete();
            });
        } else {
            interceptObservable.next(event);
            interceptObservable.complete();
        }
    }
}

protected handleErrorResponse(error: any, interceptObservable: Subject<HttpEvent<any>>): Observable<any> {
    const _this = this;
    const errorObservable = new Subject();
    if (!(error.error instanceof Blob)) {
        interceptObservable.error(error);
        interceptObservable.complete();
        return of({});
    }
    this.configuration.blobToText(error.error).subscribe(function (json) {
        const errorBody = (json === '' || json === 'null') ? {} : JSON.parse(json);
        const errorResponse = new HttpResponse({
            headers: error.headers,
            status: error.status,
            body: errorBody
        });
        const ajaxResponse = _this.configuration.getAbpAjaxResponseOrNull(errorResponse);
        if (ajaxResponse != null) {
            _this.configuration.handleAbpResponse(errorResponse, ajaxResponse);
        } else {
            _this.configuration.handleNonAbpErrorResponse(errorResponse);
        }
        errorObservable.complete();
        // prettify error object.
        error.error = errorBody;
        interceptObservable.error(error);
        interceptObservable.complete();
    });
    return errorObservable;
}

private itemExists<T>(items, predicate) {
    for (let i = 0; i < items.length; i++) {
        if (predicate(items[i])) {
            return true;
        }
    }
    return false;
}

}