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;
}
}