Angular 6 中的 Http 错误处理
Http Error Handling in Angular 6
我正在尝试使用 angular 中的以下 class 来处理 http 错误 6. 我从服务器收到了 401 未授权状态。但是无论如何,我没有看到控制台错误消息。
HttpErrorsHandler.ts 文件
import { ErrorHandler, Injectable} from '@angular/core';
@Injectable()
export class HttpErrorsHandler implements ErrorHandler {
handleError(error: Error) {
// Do whatever you like with the error (send it to the server?)
// And log it to the console
console.error('It happens: ', error);
}
}
app.module.ts 文件
providers: [{provide: ErrorHandler, useClass: HttpErrorsHandler}],
HttpCallFile
import { Injectable , Component} from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from 'rxjs';
import {AuthServiceJwt} from '../Common/sevice.auth.component';
@Injectable()
export class GenericHttpClientService {
private readonly baseUrl : string = "**********";
constructor(private httpClientModule: HttpClient , private authServiceJwt : AuthServiceJwt) {
}
public GenericHttpPost<T>(_postViewModel: T , destinationUrl : string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, _postViewModel, { headers });
}
// This method is to post Data and Get Response Data in two different type
public GenericHttpPostAndResponse<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers });
}
// This method is to post Data and Get Response Data in two different type without JWT Token
public GenericHttpPostWithOutToken<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8');
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers });
}
public GenericHttpGet<T>(destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.get<T>(this.baseUrl + destinationUrl, { headers });
}
public GenericHttpDelete<T>(destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.delete<T>(this.baseUrl + destinationUrl, { headers });
}
}
admin.user.component.ts 文件
private getUsersHttpCall(): void {
this.spinnerProgress = true;
this.genericHttpService.GenericHttpGet<GenericResponseObject<UserViewModel[]>>(this.getAdminUserUrl).subscribe(data => {
if (data.isSuccess) {
this.genericResponseObject.data = data.data;
this.dataSource = this.genericResponseObject.data
this.spinnerProgress = false;
}
}, error => {
console.log(error);
this.spinnerProgress = false;
});
}
对于 XHR 请求,您应该使用 Interceptor
这是我用来将 JWT 添加到 headers 并处理一些响应错误的工具:
import {Injectable} from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor, HttpErrorResponse
} from '@angular/common/http';
import {AuthService} from '../service/auth.service';
import {Observable, of} from 'rxjs';
import {Router} from "@angular/router";
import {catchError} from "rxjs/internal/operators";
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public auth: AuthService, private router: Router) {
}
/**
* intercept all XHR request
* @param request
* @param next
* @returns {Observable<A>}
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (localStorage.getItem('jwtToken')) {
request = request.clone({
setHeaders: {
Authorization: 'Bearer ' + localStorage.getItem('jwtToken')
}
});
}
/**
* continues request execution
*/
return next.handle(request).pipe(catchError((error, caught) => {
//intercept the respons error and displace it to the console
console.log(error);
this.handleAuthError(error);
return of(error);
}) as any);
}
/**
* manage errors
* @param err
* @returns {any}
*/
private handleAuthError(err: HttpErrorResponse): Observable<any> {
//handle your auth error or rethrow
if (err.status === 401) {
//navigate /delete cookies or whatever
console.log('handled error ' + err.status);
this.router.navigate([`/login`]);
// if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well.
return of(err.message);
}
throw err;
}
}
不要忘记像这样将拦截器注册到 app.module.ts
中:
import { TokenInterceptor } from './auth/token.interceptor';
@NgModule({
declarations: [],
imports: [],
exports: [],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true,
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
根据@firegloves 的回答,为了让各个服务中的 .pipe
处理程序实际上能够 catchError
它们自己的 HTTP 故障代码,您需要将代码结构化为:
- 如果拦截器检测到 HTTP 401 错误代码,处理它并 return
of(error)
这可能会在任何后续 .pipe
中正常处理。
- 如果 HTTP 错误代码是拦截器无法处理的代码,
throw
将再次处理它,以便后续的错误处理程序(如您的服务中的错误处理程序)可以为其处理非 401 错误自己的电话。
我的拦截器有双重工作。它:
- 如果存储了令牌,则将
Authorization
header 注入所有传出请求。
- 拦截 HTTP 401 未经身份验证的错误,此时它使令牌存储无效,然后重定向回
/login
。
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root',
})
export class RequestInterceptor implements HttpInterceptor {
constructor(
private readonly auth: AuthService,
private readonly router: Router,
) {
}
/**
* @param HttpRequest<any> request - The intercepted request
* @param HttpHandler next - The next interceptor in the pipeline
* @return Observable<HttpEvent<any>>
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = this.addToken(request);
return next.handle(request)
// add error handling
.pipe(
catchError(
(error: any, caught: Observable<HttpEvent<any>>) => {
if (error.status === 401) {
this.handleAuthError();
// if you've caught / handled the error, you don't
// want to rethrow it unless you also want
// downstream consumers to have to handle it as
// well.
return of(error);
}
throw error;
}
),
);
}
/**
* Handle API authentication errors.
*/
private handleAuthError() {
// clear stored credentials; they're invalid
this.auth.credentials = null;
// navigate back to the login page
this.router.navigate(['/login']);
}
/**
* Add stored auth token to request headers.
* @param HttpRequest<any> request - the intercepted request
* @return HttpRequest<any> - the modified request
*/
private addToken(request: HttpRequest<any>): HttpRequest<any> {
const token: string = this.auth.token;
if (token) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
return request;
}
}
所有 AuthService
所做的就是有一个 public get/set 将凭据 object 粘贴到 localStorage
- 它确保令牌没有过期, 但你可以随心所欲地设计它。
如@firegloves 所述,您必须将拦截器添加到 app.module.ts
中的管道:
import { RequestInterceptor } from './auth/request.interceptor';
@NgModule({
declarations: [],
imports: [],
exports: [],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: RequestInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule { }
angular 7
中的示例错误处理
public getstats(param: string, confgId: number, startDate: Date, endDate: Date): Observable<string> {
return this.http.post(this.stats, {
param: param,
endDate: endDate
}).pipe(tap((stats: string) => console.log('Got Exceedance Stats : ' + JSON.stringify(stats))),
catchError(this.handleError<string>('stats')));
}
下面是错误处理程序
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
console.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
我正在尝试使用 angular 中的以下 class 来处理 http 错误 6. 我从服务器收到了 401 未授权状态。但是无论如何,我没有看到控制台错误消息。
HttpErrorsHandler.ts 文件
import { ErrorHandler, Injectable} from '@angular/core';
@Injectable()
export class HttpErrorsHandler implements ErrorHandler {
handleError(error: Error) {
// Do whatever you like with the error (send it to the server?)
// And log it to the console
console.error('It happens: ', error);
}
}
app.module.ts 文件
providers: [{provide: ErrorHandler, useClass: HttpErrorsHandler}],
HttpCallFile
import { Injectable , Component} from '@angular/core';
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from 'rxjs';
import {AuthServiceJwt} from '../Common/sevice.auth.component';
@Injectable()
export class GenericHttpClientService {
private readonly baseUrl : string = "**********";
constructor(private httpClientModule: HttpClient , private authServiceJwt : AuthServiceJwt) {
}
public GenericHttpPost<T>(_postViewModel: T , destinationUrl : string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, _postViewModel, { headers });
}
// This method is to post Data and Get Response Data in two different type
public GenericHttpPostAndResponse<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers });
}
// This method is to post Data and Get Response Data in two different type without JWT Token
public GenericHttpPostWithOutToken<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8');
return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers });
}
public GenericHttpGet<T>(destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.get<T>(this.baseUrl + destinationUrl, { headers });
}
public GenericHttpDelete<T>(destinationUrl: string): Observable<T> {
const headers = new HttpHeaders().set('Content-Type', 'application/json')
.set('Authorization',`Bearer ${this.authServiceJwt.getToken}`);
return this.httpClientModule.delete<T>(this.baseUrl + destinationUrl, { headers });
}
}
admin.user.component.ts 文件
private getUsersHttpCall(): void {
this.spinnerProgress = true;
this.genericHttpService.GenericHttpGet<GenericResponseObject<UserViewModel[]>>(this.getAdminUserUrl).subscribe(data => {
if (data.isSuccess) {
this.genericResponseObject.data = data.data;
this.dataSource = this.genericResponseObject.data
this.spinnerProgress = false;
}
}, error => {
console.log(error);
this.spinnerProgress = false;
});
}
对于 XHR 请求,您应该使用 Interceptor
这是我用来将 JWT 添加到 headers 并处理一些响应错误的工具:
import {Injectable} from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor, HttpErrorResponse
} from '@angular/common/http';
import {AuthService} from '../service/auth.service';
import {Observable, of} from 'rxjs';
import {Router} from "@angular/router";
import {catchError} from "rxjs/internal/operators";
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public auth: AuthService, private router: Router) {
}
/**
* intercept all XHR request
* @param request
* @param next
* @returns {Observable<A>}
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (localStorage.getItem('jwtToken')) {
request = request.clone({
setHeaders: {
Authorization: 'Bearer ' + localStorage.getItem('jwtToken')
}
});
}
/**
* continues request execution
*/
return next.handle(request).pipe(catchError((error, caught) => {
//intercept the respons error and displace it to the console
console.log(error);
this.handleAuthError(error);
return of(error);
}) as any);
}
/**
* manage errors
* @param err
* @returns {any}
*/
private handleAuthError(err: HttpErrorResponse): Observable<any> {
//handle your auth error or rethrow
if (err.status === 401) {
//navigate /delete cookies or whatever
console.log('handled error ' + err.status);
this.router.navigate([`/login`]);
// if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well.
return of(err.message);
}
throw err;
}
}
不要忘记像这样将拦截器注册到 app.module.ts
中:
import { TokenInterceptor } from './auth/token.interceptor';
@NgModule({
declarations: [],
imports: [],
exports: [],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true,
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
根据@firegloves 的回答,为了让各个服务中的 .pipe
处理程序实际上能够 catchError
它们自己的 HTTP 故障代码,您需要将代码结构化为:
- 如果拦截器检测到 HTTP 401 错误代码,处理它并 return
of(error)
这可能会在任何后续.pipe
中正常处理。 - 如果 HTTP 错误代码是拦截器无法处理的代码,
throw
将再次处理它,以便后续的错误处理程序(如您的服务中的错误处理程序)可以为其处理非 401 错误自己的电话。
我的拦截器有双重工作。它:
- 如果存储了令牌,则将
Authorization
header 注入所有传出请求。 - 拦截 HTTP 401 未经身份验证的错误,此时它使令牌存储无效,然后重定向回
/login
。
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root',
})
export class RequestInterceptor implements HttpInterceptor {
constructor(
private readonly auth: AuthService,
private readonly router: Router,
) {
}
/**
* @param HttpRequest<any> request - The intercepted request
* @param HttpHandler next - The next interceptor in the pipeline
* @return Observable<HttpEvent<any>>
*/
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = this.addToken(request);
return next.handle(request)
// add error handling
.pipe(
catchError(
(error: any, caught: Observable<HttpEvent<any>>) => {
if (error.status === 401) {
this.handleAuthError();
// if you've caught / handled the error, you don't
// want to rethrow it unless you also want
// downstream consumers to have to handle it as
// well.
return of(error);
}
throw error;
}
),
);
}
/**
* Handle API authentication errors.
*/
private handleAuthError() {
// clear stored credentials; they're invalid
this.auth.credentials = null;
// navigate back to the login page
this.router.navigate(['/login']);
}
/**
* Add stored auth token to request headers.
* @param HttpRequest<any> request - the intercepted request
* @return HttpRequest<any> - the modified request
*/
private addToken(request: HttpRequest<any>): HttpRequest<any> {
const token: string = this.auth.token;
if (token) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
return request;
}
}
所有 AuthService
所做的就是有一个 public get/set 将凭据 object 粘贴到 localStorage
- 它确保令牌没有过期, 但你可以随心所欲地设计它。
如@firegloves 所述,您必须将拦截器添加到 app.module.ts
中的管道:
import { RequestInterceptor } from './auth/request.interceptor';
@NgModule({
declarations: [],
imports: [],
exports: [],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: RequestInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule { }
angular 7
中的示例错误处理public getstats(param: string, confgId: number, startDate: Date, endDate: Date): Observable<string> {
return this.http.post(this.stats, {
param: param,
endDate: endDate
}).pipe(tap((stats: string) => console.log('Got Exceedance Stats : ' + JSON.stringify(stats))),
catchError(this.handleError<string>('stats')));
}
下面是错误处理程序
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
console.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}