HTTP 客户端
Angular 提供了 HttpClient 模块来处理 HTTP 请求。它支持请求和响应拦截、类型化响应、错误处理等功能。本章将详细介绍如何使用 HTTP 客户端与后端 API 交互。
配置 HttpClient
首先在应用配置中启用 HTTP 客户端:
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};
基本使用
GET 请求
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface User {
id: number;
name: string;
email: string;
}
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = 'https://api.example.com/users';
// 获取所有用户
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
// 获取单个用户
getUser(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}
// 带查询参数
searchUsers(keyword: string): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl, {
params: { q: keyword }
});
}
}
POST 请求
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = 'https://api.example.com/users';
// 创建用户
createUser(user: Omit<User, 'id'>): Observable<User> {
return this.http.post<User>(this.apiUrl, user);
}
// 带选项的 POST 请求
createUserWithOptions(user: Omit<User, 'id'>): Observable<User> {
return this.http.post<User>(this.apiUrl, user, {
headers: { 'Content-Type': 'application/json' },
observe: 'response' // 获取完整响应
});
}
}
PUT 和 PATCH 请求
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = 'https://api.example.com/users';
// 完整更新(PUT)
updateUser(user: User): Observable<User> {
return this.http.put<User>(`${this.apiUrl}/${user.id}`, user);
}
// 部分更新(PATCH)
patchUser(id: number, changes: Partial<User>): Observable<User> {
return this.http.patch<User>(`${this.apiUrl}/${id}`, changes);
}
}
DELETE 请求
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
private apiUrl = 'https://api.example.com/users';
// 删除用户
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}
在组件中使用
使用 Observable
import { Component, inject, OnInit } from '@angular/core';
import { UserService, User } from './user.service';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-user-list',
template: `
@if (users$ | async; as users) {
<ul>
@for (user of users; track user.id) {
<li>{{ user.name }} - {{ user.email }}</li>
}
</ul>
} @else {
<p>加载中...</p>
}
`,
imports: [CommonModule]
})
export class UserListComponent implements OnInit {
private userService = inject(UserService);
users$!: Observable<User[]>;
ngOnInit() {
this.users$ = this.userService.getUsers();
}
}
使用 Signals
import { Component, inject, signal } from '@angular/core';
import { UserService, User } from './user.service';
@Component({
selector: 'app-user-list',
template: `
@if (loading()) {
<p>加载中...</p>
} @else if (error()) {
<p class="error">{{ error() }}</p>
} @else {
<ul>
@for (user of users(); track user.id) {
<li>{{ user.name }} - {{ user.email }}</li>
}
</ul>
}
`
})
export class UserListComponent {
private userService = inject(UserService);
users = signal<User[]>([]);
loading = signal(true);
error = signal<string | null>(null);
constructor() {
this.loadUsers();
}
loadUsers() {
this.loading.set(true);
this.error.set(null);
this.userService.getUsers().subscribe({
next: (data) => {
this.users.set(data);
this.loading.set(false);
},
error: (err) => {
this.error.set(err.message);
this.loading.set(false);
}
});
}
}
错误处理
基本错误处理
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUser(id: number) {
return this.http.get<User>(`/api/users/${id}`).pipe(
catchError(this.handleError)
);
}
private handleError(error: HttpErrorResponse) {
let errorMessage = '发生未知错误';
if (error.error instanceof ErrorEvent) {
// 客户端错误
errorMessage = `客户端错误: ${error.error.message}`;
} else {
// 服务端错误
switch (error.status) {
case 401:
errorMessage = '未授权,请登录';
break;
case 403:
errorMessage = '禁止访问';
break;
case 404:
errorMessage = '资源不存在';
break;
case 500:
errorMessage = '服务器内部错误';
break;
default:
errorMessage = `服务器返回错误: ${error.status}`;
}
}
return throwError(() => new Error(errorMessage));
}
}
重试机制
import { retry, catchError } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
// 失败后重试 3 次
getUser(id: number) {
return this.http.get<User>(`/api/users/${id}`).pipe(
retry(3),
catchError(this.handleError)
);
}
}
HTTP 拦截器
拦截器可以在请求发送前和响应返回后进行处理。
创建拦截器
import { Injectable, inject } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpInterceptorFn
} from '@angular/common/http';
import { Observable } from 'rxjs';
// 函数式拦截器(推荐)
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
// 添加认证 token
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${auth.getToken()}`
}
});
return next(authReq);
};
// 日志拦截器
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
console.log(`请求: ${req.method} ${req.url}`);
return next(req).pipe(
tap({
next: (event) => {
if (event instanceof HttpResponse) {
console.log(`响应: ${event.status}`);
}
},
error: (error) => {
console.error(`请求错误: ${error.message}`);
}
})
);
};
注册拦截器
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor, loggingInterceptor } from './interceptors';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([authInterceptor, loggingInterceptor])
)
]
};
缓存拦截器
import { Injectable, inject } from '@angular/core';
import { HttpInterceptorFn, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable, of, tap } from 'rxjs';
const cache = new Map<string, HttpResponse<any>>();
export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
// 只缓存 GET 请求
if (req.method !== 'GET') {
return next(req);
}
const cached = cache.get(req.urlWithParams);
if (cached) {
console.log('从缓存返回:', req.url);
return of(cached.clone());
}
return next(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
cache.set(req.urlWithParams, event.clone());
}
})
);
};
文件上传
上传单个文件
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class FileService {
private http = inject(HttpClient);
uploadFile(file: File): Observable<{ url: string }> {
const formData = new FormData();
formData.append('file', file);
return this.http.post<{ url: string }>('/api/upload', formData);
}
}
在组件中使用:
import { Component, inject } from '@angular/core';
import { FileService } from './file.service';
@Component({
selector: 'app-file-upload',
template: `
<input type="file" #fileInput (change)="onFileSelected(fileInput.files)">
<button (click)="upload()" [disabled]="!selectedFile">上传</button>
@if (uploadProgress > 0) {
<progress [value]="uploadProgress" max="100"></progress>
}
`
})
export class FileUploadComponent {
private fileService = inject(FileService);
selectedFile: File | null = null;
uploadProgress = 0;
onFileSelected(files: FileList | null) {
this.selectedFile = files?.[0] ?? null;
}
upload() {
if (!this.selectedFile) return;
this.fileService.uploadFile(this.selectedFile).subscribe({
next: (result) => {
console.log('上传成功:', result.url);
},
error: (error) => {
console.error('上传失败:', error);
}
});
}
}
上传带进度
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpEventType } from '@angular/common/http';
import { Observable, map } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class FileService {
private http = inject(HttpClient);
uploadWithProgress(file: File): Observable<number> {
const formData = new FormData();
formData.append('file', file);
return this.http.post('/api/upload', formData, {
reportProgress: true,
observe: 'events'
}).pipe(
map(event => {
if (event.type === HttpEventType.UploadProgress && event.total) {
return Math.round((event.loaded / event.total) * 100);
}
return 0;
})
);
}
}
小结
- HttpClient 是 Angular 内置的 HTTP 客户端,支持类型化响应
- 请求方法包括 GET、POST、PUT、PATCH、DELETE
- 错误处理使用
catchError操作符统一处理 - 拦截器可以在请求/响应流程中添加通用逻辑
- 文件上传使用 FormData,支持进度监控
下一步
掌握了 HTTP 客户端后,接下来学习 管道 Pipes,了解如何转换显示数据。