qrcode

马上订阅,开启修仙之旅

Angular 6 HttpClient 快速入门

本教程将介绍如何在 Angular 6.x 中使用 HttpClient 发送 Http 请求,如 get、post、put 和 delete 请求。在 Angular 4.3+ 版本之后引入了 HttpClientModule 模块,该模块提供的 HttpClient 服务是已有 Angular HTTP API 的演进,它在一个单独的 @angular/common/http 包中。

废话不多说,现在让我们来看一下如何在 Angular 6.x 中使用 HttpClientModule 模块。

导入 HttpClientModule 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";

import { AppComponent } from "./app.component";

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}

需要注意的是,现在 JSON 是默认的数据格式,我们不需要再进行显式的解析。即我们不需要再使用以下代码:

1
http.get(url).map(res => res.json()).subscribe(...)

现在我们可以这样写:

1
http.get(url).subscribe(...)

发送 Get 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Component, OnInit } from "@angular/core";
import { HttpClient, HttpParams, HttpHeaders } from "@angular/common/http";

import { Observable } from "rxjs";
import { tap } from "rxjs/operators";

interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
}

@Component({
selector: "app-root",
template: `
<ul *ngIf="todos$ | async as todos else noData">
<li *ngFor="let todo of todos">
<span>Title: {{todo.title}}</span> ——
<span>Completed: {{todo.completed}}</span>
</li>
</ul>
<ng-template #noData>No Data Available</ng-template>
`
})
export class AppComponent implements OnInit {
todos$: Observable<Todo[]>;
constructor(private http: HttpClient) {}

ngOnInit() {
this.todos$ = this.http
.get<Todo[]>(
"https://jsonplaceholder.typicode.com/todos?_page=1&_limit=10"
)
.pipe(tap(console.log));
}
}

设置查询参数

假设发送 Get 请求时,需要设置对应的查询参数,预期的 URL 地址如下:

1
https://jsonplaceholder.typicode.com/todos?_page=1&_limit=10

创建 HttpParams 对象

1
2
3
4
5
6
7
8
9
import { HttpClient, HttpParams } from "@angular/common/http";

const params = new HttpParams().set("_page", "1").set("_limit", "10");

ngOnInit() {
this.todos$ = this.http
.get<Todo[]>("https://jsonplaceholder.typicode.com/todos", { params })
.pipe(tap(console.log));
}

需要注意的是,我们通过链式语法调用 set() 方法,构建 HttpParams 对象。这是因为 HttpParams 对象是不可变的,通过 set() 方法可以防止该对象被修改。

每当调用 set() 方法,将会返回包含新值的 HttpParams 对象,因此如果使用下面的方式,将不能正确的设置参数。

1
2
3
4
const params = new HttpParams();

params.set("_page", "1")
params.set("_limit", "10");

使用 fromString

1
const params = new HttpParams({fromString: "_page=1&_limit=10"});

使用 fromObject

1
const params = new HttpParams({ fromObject: { _page: "1", _limit: "10" } });

使用 request API

1
2
3
4
5
ngOnInit() {
this.users$ = this.http
.request("GET", "https://jsonplaceholder.typicode.com/todos", { params })
.pipe(tap(console.log));
}

获取完整响应

默认情况下,HttpClient 服务返回的是响应体,有时候我们需要获取响应头的相关信息,这时你可以设置请求 options 对象的 observe 属性值为 response 来获取完整的响应对象。

1
2
3
4
5
6
this.http.get("https://jsonplaceholder.typicode.com/todos/1", {
observe: "response"
})
.subscribe(res => {
console.dir("Response: " + res.status);
});

设置响应类型

如果你期望的响应对象的格式不是 JSON,你可以通过 responseType 属性来设定响应类型,比如:

1
2
3
4
5
this.http.get("https://jsonplaceholder.typicode.com/todos/1", {
responseType: "text"
}).subscribe(text => {
console.log("Response: " + text);
});

需要注意的是除了支持 json 和 text 类型外,还支持 arraybuffer 和 blob 类型。

设置 Http Headers

1
2
3
4
5
6
7
8
9
10
11
const params = new HttpParams({ fromObject: { _page: "1", _limit: "10" } });
const headers = new HttpHeaders().set("token", "iloveangular");

ngOnInit() {
this.todos$ = this.http
.get<Todo[]>("https://jsonplaceholder.typicode.com/todos", {
headers,
params
})
.pipe(tap(console.log));
}

发送 Put 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const headers = new HttpHeaders().set(
"Content-type",
"application/json; charset=UTF-8"
);

updateFirstTodo() {
this.http
.put(
"https://jsonplaceholder.typicode.com/todos/1",
{
userId: 1,
id: 1,
title: "delectus aut autem",
completed: true
},
{ headers }
)
.subscribe(
val => {
console.log("Put call successful value returned in body", val);
},
error => {
console.log("Put call in error", error);
},
() => {
console.log("The PUT observable is now completed.");
}
);
}

发送 Patch 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const headers = new HttpHeaders().set(
"Content-type",
"application/json; charset=UTF-8"
);

patchFirstTodo() {
this.http
.patch(
"https://jsonplaceholder.typicode.com/todos/1",
{
title: "learn angular 7"
},
{ headers }
)
.subscribe(
val => {
console.log("Patch call successful value returned in body", val);
},
error => {
console.log("Patch call in error", error);
},
() => {
console.log("The Patch observable is now completed.");
}
);
}

发送 Delete 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const headers = new HttpHeaders().set(
"Content-type",
"application/json; charset=UTF-8"
);

deleteFirstTodo() {
this.http
.delete("https://jsonplaceholder.typicode.com/todos/1", {
headers
})
.subscribe(
val => {
console.log("Delete call successful value returned in body", val);
},
error => {
console.log("Delete call in error", error);
},
() => {
console.log("The Delete observable is now completed.");
}
);
}

发送 Post 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const headers = new HttpHeaders().set(
"Content-type",
"application/json; charset=UTF-8"
);

createNewTodo() {
this.http
.post(
"https://jsonplaceholder.typicode.com/todos",
{
userId: 1,
title: "learn ionic 4",
completed: false
},
{ headers }
)
.subscribe(
val => {
console.log("Post call successful value returned in body", val);
},
error => {
console.log("Post call in error", error);
},
() => {
console.log("The Post observable is now completed.");
}
);
}

并行发送多个 Http 请求

1
2
3
4
5
6
7
8
9
10
parallelRequests() {
const parallel$ = forkJoin(
this.http.get("https://jsonplaceholder.typicode.com/users/1"),
this.http.get("https://jsonplaceholder.typicode.com/todos/1")
);

parallel$.subscribe(values => {
console.log("all values", values);
});
}

顺序发送 Http 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sequentialRequests() {
const sequence$ = this.http
.get<Todo>("https://jsonplaceholder.typicode.com/todos/1")
.pipe(
switchMap(todo => {
todo.title += " - TEST ";
return this.http.put("https://jsonplaceholder.typicode.com/todos/1", todo);
})
);

sequence$.subscribe(val => {
console.log("Put call successful value returned in body", val);
});
}

获取顺序发送 Http 请求的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sequentialRequests() {
const sequence$ = this.http
.get<Todo>("https://jsonplaceholder.typicode.com/todos/1")
.pipe(
switchMap(
todo => {
const newTitle = todo.title + " - TEST ";
const newTodo = { ...todo, title: newTitle };
return this.http.put(
"https://jsonplaceholder.typicode.com/todos/1",
newTodo
);
},
(firstHTTPResult, secondHTTPResult) => [
firstHTTPResult,
secondHTTPResult
]
)
);

sequence$.subscribe(val => {
console.log("Put call successful value returned in body", val);
});
}

请求异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { of } from "rxjs";
import { catchError } from "rxjs/operators";

throwError() {
this.http
.get("https://jsonplaceholder.typicode.com/simulate-error")
.pipe(
catchError(error => {
console.error("Error catched", error);
return of({ description: "Error Value Emitted" });
})
)
.subscribe(
val => console.log("Value emitted successfully", val),
error => {
console.error("This line is never called ", error);
},
() => console.log("HTTP Observable completed...")
);
}

当发生异常时,控制台的输出结果:

1
2
3
4
Error catched 
HttpErrorResponse {headers: HttpHeaders, status: 404, ...}
Value emitted successfully {description: "Error Value Emitted"}
HTTP Observable completed...

Http 拦截器

定义拦截器

auth.interceptor.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Injectable } from "@angular/core";
import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor } from "@angular/common/http";

import { Observable } from "rxjs";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const clonedRequest = req.clone({
headers: req.headers.set("X-CustomAuthHeader", "iloveangular")
});
console.log("new headers", clonedRequest.headers.keys());
return next.handle(clonedRequest);
}
}

应用拦截器

1
2
3
4
5
6
7
8
9
10
11
import { AuthInterceptor } from "./interceptors/auth.interceptor";

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule {}

Http 进度事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getData() {
this.http
.get("https://jsonplaceholder.typicode.com/todos", {
observe: 'events',
reportProgress: true
})
.subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log("Request sent!");
break;
case HttpEventType.ResponseHeader:
console.log("Response header received!");
break;
case HttpEventType.DownloadProgress:
const kbLoaded = Math.round(event.loaded / 1024);
console.log(`Download in progress! ${kbLoaded}Kb loaded`);
break;
case HttpEventType.Response:
console.log("Done!", event.body);
}
});
}

以上代码成功运行后,在控制台会输出以下信息:

1
2
3
4
5
Request sent!
Response header received!
Download in progress! 6Kb loaded
Download in progress! 24Kb loaded
Done!

总结

本文通过 jsonplaceholder 提供的 API,介绍了如何使用 HttpClientModule 模块中的 HttpClient 服务,发送 Get、Post、Delete 等请求,同时介绍了如何利用 RxJS 处理并行和顺序 Http 请求。感兴趣的同学,可以查看 Stackblitz 线上示例。


欢迎小伙伴们订阅前端修仙之路,及时阅读 Angular、RxJS、TypeScript 和 Node.js 最新文章。

qrcode