前端开发,axios 是标配的http 请求发起libary, 采用的是Promise 的方式。然后,Angular中采用的是另外一种形式Observable 观察订阅模式 。Angular默认推荐采用内置的HttpClient ,也就是 @angular/common/http 中的 HttpClient 服务类,用于与服务器进行通讯。
HTTP 客户端服务提供了以下主要功能。
-
请求类型化响应对象的能力。 -
简化的错误处理。 -
各种特性的可测试性。 -
请求和响应的拦截机制。
模块引入
要想使用 HttpClient ,就要先导入 Angular 的 HttpClientModule 。大多数应用都会在根模块 AppModule 中导入它。如下:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
],
declarations: [
AppComponent,
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
然后,你可以把 HttpClient 服务注入成一个应用类的依赖项:ajax.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class ConfigService {
constructor(private http: HttpClient) { }
}
get 请求数据
使用 HttpClient.get() 方法从服务器获取数据。该异步方法会发送一个 HTTP 请求,并返回一个 Observable可观察对象 ,它会在收到响应时发出所请求到的数据。
get() 方法有两个参数。要获取的端点 URL,以及一个可以用来配置请求的options 选项对象。
options: {
headers?: HttpHeaders | {[header: string]: string | string[]},
observe?: 'body' | 'events' | 'response',
params?: HttpParams|{[param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>},
reportProgress?: boolean,
responseType?: 'arraybuffer'|'blob'|'json'|'text',
withCredentials?: boolean,
}
演示get 请求数据
this.http.get('/api/data', {
params: new HttpParams().set('page', "1").set('limit', "50"),
headers: new HttpHeaders({'Authorization': 'my-auth-token'})
}).subscribe((data: any) => console.log(data));
post 请求
this.http.post(
'/api/data',
{ "params1": 1, "params2": 2 },
{ headers: new HttpHeaders({'Authorization': 'my-auth-token'}) }
).subscribe(
res => { console.log("POST call successful value returned in body", res); },
error => { console.log("POST call in error", error); },
() => { console.log("The POST observable is now completed.");
);
put 请求
this.http.put(
'/api/data',
{ "params1": 1, "params2": 2 },
{ headers: new HttpHeaders({'Authorization': 'my-auth-token'}) }
).subscribe(
res => { console.log("POST call successful value returned in body", res); },
error => { console.log("POST call in error", error); },
() => { console.log("The POST observable is now completed.");
);
delete 请求
const url = `${this.url}/${id}`
this.http.delete(
url,
{ headers: new HttpHeaders({'Authorization': 'my-auth-token' }
).subscribe(
res => { console.log("POST call successful value returned in body", res); },
error => { console.log("POST call in error", error); },
() => { console.log("The POST observable is now completed.");
);
JSONP 请求
当服务器不支持 CORS 协议时,应用程序可以使用 HttpClient 跨域发出 JSONP 请求。
this.http.jsonp('/api/handle', 'callback').pipe(
catchError(this.handleError('searchHeroes', []))
).subscribe(
res => { console.log("POST call successful value returned in body", res); },
error => { console.log("POST call in error", error); },
() => { console.log("The POST observable is now completed.");
);
请求错误处理
如果请求在服务器上失败了,那么HttpClient 就会返回一个错误对象(HttpErrorResponse )而不是一个成功的响应对象(HttpResponse )。 如下:以get ·请求为例,在ajax.service.ts 中添加错误处理器
private handleError(error: HttpErrorResponse) {
if(error instanceof HttpErrorResponse) {
if (error.status === 0) {
console.error('An error occurred:', error.error);
} else {
console.error(`Backend returned code ${error.status}, ` + `body was: ${error.error}`);
}
return throwError('Something bad happened; please try again later.');
}
}
this.http.get(this.configUrl).pipe(
retry(3),
catchError(this.handleError)
).subscribe(res => console.log(res))
处理错误请求一般在拦截器统一处理
拦截请求和响应
在上面的请求中,每一个请求都要单独为请求头加入认证字段,增加代码冗余,可以直接在请求拦截器中配置好请求头,而不用每次请求都单独配置。
要实现拦截器,就要实现一个实现了 HttpInterceptor 接口中的 intercept() 方法的类。如下:default.interceptor.ts
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
export class DefaultInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log(req)
const authToken = localStorage.getItem("token")
const authReq = req.clone({
headers: req.headers.set('Authorization', authToken)
});
console.log(authReq)
return next.handle(authReq);
}
}
intercept 要求我们返回一个Observable 对象供组件订阅使用,该方法有两个形参:
req :HttpRequest类型,这个是我们http请求对象;next :HttpHandler类型,next 对象表示拦截器链表中的下一个拦截器,它提供了handle() 方法,可以把HTTP请求转换成HttpEvent 类型的Observable , 这个链表中的最后一个 next 对象就是 HttpClient 的后端返回的response
声明之后我们需要在app.module.ts 根模块注入这个拦截器。如下:
import { DefaultInterceptor } from '@core/net/default.interceptor';
import { HttpClient, HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
],
declarations: [
AppComponent,
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
multi: true 选项。 这个必须的选项会告诉 Angular HTTP_INTERCEPTORS 是一个多重提供者的令牌,表示它会注入一个多值的数组,而不是单一的值。
集中维护拦截器
当声明的拦截器越来越多时不好维护,可以专门建立一个文件夹来维护这些拦截器,并提供一个index.ts 文件把所有拦截器封装成一个数组对象然后暴露出来,最后再在app.module.ts 中注入这些拦截器,如下:目录结构
而在index.ts 中如下:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { DefaultInterceptor } from './default.interceptor'
import { BloodInterceptor } from './blood.interceptor'
...
export const HttpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: DefaultInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: BloodInterceptor, multi: true },
...
]
最后在app.module.ts 根模块中注入
import { HttpInterceptorProviders } from './interceptor/index';
@NgModule({
imports: [
...
],
declarations: [
AppComponent,
],
providers: [
...HttpInterceptorProviders,
],
bootstrap: [AppComponent]
})
export class AppModule { }
拦截器多了之后,它的执行顺序其实就是export 的这个数组的索引下标顺序0,1,2…
捕获响应错误,集中处理
当每一次请求发生错误的时候,我们一般都要进行错误处理,但是每次都对每个单独错误处理很麻烦,可以专门添加一个错误处理拦截器,如下:catchError.injector.ts
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { catchError, retry } from 'rxjs/operators';
export class CatchErrorInjector implements HttpInterceptor {
private handleError(error: HttpErrorResponse) {
if(error instanceof HttpErrorResponse) {
}
}
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
retry(3),
catchError(err => {
console.log(err, '后端接口报错');
this.handleError(err)
})
);
}
}
|