09_http服务介绍
Angular v20 HTTP 服务:后端交互核心实践
HTTP 服务是 Angular 应用与后端 API 通信的核心桥梁,负责数据的获取、提交与同步。Angular v20 基于@angular/common/http模块重构了 HTTP 能力,通过provideHttpClient简化配置,强化与 Signals 的联动,并优化请求性能。本章将从基础配置到高级实战,全面解析 HTTP 服务的使用方法。
1. HTTP 服务基础:配置与核心类
1.1 环境配置:替代HttpClientModule的provideHttpClient
Angular v20 摒弃了传统的HttpClientModule,改用函数式配置provideHttpClient启用 HTTP 服务,支持按需添加拦截器与特性,更符合树摇优化原则。
全局配置(应用级注入)
在main.ts中通过bootstrapApplication配置全局 HTTP 服务:
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { provideHttpClient } from '@angular/common/http'; // 核心HTTP配置函数
import { provideZonelessChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(), // 启用基础HTTP服务
provideZonelessChangeDetection() // 结合无Zone.js架构
]
}).catch(err => console.error(err));
独立组件局部配置(可选)
对于需要特殊配置的独立组件,可在组件元数据中补充 HTTP 依赖:
// user.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { provideHttpClient } from '@angular/common/http';
@Component({
selector: 'app-user',
standalone: true,
providers: [provideHttpClient()], // 组件级HTTP配置(极少用,优先全局)
template: `<p>{{ user.name }}</p>`
})
export class UserComponent {
user = { name: '' };
constructor(private http: HttpClient) {}
}
1.2 核心类与注入方式
核心类说明
HttpClient:发送 HTTP 请求的核心服务,提供get()/post()等请求方法HttpHeaders:配置请求头(如认证令牌、Content-Type)HttpParams:拼接 URL 查询参数HttpEvent:请求生命周期事件(如进度、响应)
注入方式
支持两种注入方式,v20 推荐使用inject()函数简化代码:
方式1:构造函数注入(传统)
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({ selector: 'app-data', standalone: true, template: '' })
export class DataComponent {
constructor(private http: HttpClient) {} // 构造函数注入
}
方式2:inject()函数注入(v20推荐,支持方法内注入)
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({ selector: 'app-data', standalone: true, template: '' })
export class DataComponent {
private http = inject(HttpClient); // 函数式注入,更灵活
fetchData() {
this.http.get('/api/data').subscribe();
}
}
2. 基础请求方法:GET/POST/PUT/DELETE 实战
HttpClient封装了所有 HTTP 标准方法,返回Observable流,可结合 Signals 实现响应式数据更新。
2.1 GET 请求:获取数据
用于查询数据,支持 URL 参数、请求头配置,常与toSignal()转换为响应式信号。
基本用法(无参数)
import { Component, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { toSignal } from '@angular/core/rxjs-interop'; // 需导入rxjs-interop
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-list',
standalone: true,
template: `
@for (user of users(); track user.id) {
<div>{{ user.name }} ({{ user.email }})</div>
} @empty {
<div>加载中...</div>
}
`
})
export class UserListComponent {
private http = inject(HttpClient);
// 将Observable转换为Signal,初始值为空数组
users = toSignal<User[]>(this.http.get<User[]>('/api/users'), { initialValue: [] });
}
带查询参数的 GET 请求
使用HttpParams配置 URL 参数(如分页、筛选):
import { HttpParams } from '@angular/common/http';
// 方式1:直接拼接参数
this.http.get<User[]>('/api/users', {
params: new HttpParams().set('page', '1').set('limit', '10')
});
// 方式2:对象简写(v20支持)
this.http.get<User[]>('/api/users', {
params: { page: '1', limit: '10' } // 自动转换为HttpParams
});
2.2 POST 请求:提交数据
用于创建资源,需传递请求体(body),常见于表单提交场景。
基础 POST 请求
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormGroup, FormControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-user-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="userForm" (ngSubmit)="createUser()">
<input formControlName="name" placeholder="姓名">
<input formControlName="email" placeholder="邮箱">
<button type="submit">创建用户</button>
</form>
`
})
export class UserFormComponent {
private http = inject(HttpClient);
userForm = new FormGroup({
name: new FormControl(''),
email: new FormControl('')
});
createUser() {
if (this.userForm.valid) {
this.http.post<User>('/api/users', this.userForm.value) // 第二个参数为请求体
.subscribe({
next: (newUser) => console.log('创建成功:', newUser),
error: (err) => console.error('创建失败:', err)
});
}
}
}
自定义请求头
通过headers配置特殊请求头(如 JSON 格式、认证):
import { HttpHeaders } from '@angular/common/http';
const headers = new HttpHeaders({
'Content-Type': 'application/json', // 默认为JSON,可省略
'Authorization': 'Bearer your-token-here' // 认证令牌
});
this.http.post<User>('/api/users', this.userForm.value, { headers });
2.3 PUT/DELETE 请求:更新与删除资源
- PUT:全量更新资源,需传递完整资源数据
- DELETE:删除资源,通常通过 URL 传递资源 ID
// PUT:更新用户
updateUser(id: number, updatedData: Partial<User>) {
this.http.put<User>(`/api/users/${id}`, updatedData)
.subscribe(updatedUser => console.log('更新成功:', updatedUser));
}
// DELETE:删除用户
deleteUser(id: number) {
this.http.delete(`/api/users/${id}`)
.subscribe(() => console.log(`用户${id}删除成功`));
}
3. 高级特性:拦截器、错误处理与请求优化
3.1 HTTP 拦截器:统一请求 / 响应处理
拦截器(Interceptor)是 Angular HTTP 服务的核心扩展点,用于全局处理所有请求 / 响应,如添加认证令牌、统一错误处理、日志记录等。
步骤 1:创建拦截器
实现HttpInterceptor接口,重写intercept()方法:
// auth.interceptor.ts(认证拦截器)
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpInterceptor
} from '@angular/common/http';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
// 拦截请求并添加认证令牌
intercept(req: HttpRequest<any>, next: HttpHandler) {
// 1. 获取本地存储的令牌
const token = localStorage.getItem('auth_token');
// 2. 克隆请求(请求对象不可变,需通过clone修改)
if (token) {
const authReq = req.clone({
headers: req.headers.set('Authorization', `Bearer ${token}`)
});
// 3. 传递克隆后的请求
return next.handle(authReq);
}
// 无令牌时直接传递原始请求
return next.handle(req);
}
}
步骤 2:注册拦截器
通过provideHttpClient的withInterceptors()方法注册(v20 新方式):
// main.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withInterceptors([AuthInterceptor]) // 注册拦截器
)
]
});
常用拦截器场景
错误拦截器:统一捕获 HTTP 错误
// error.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
import { Router } from '@angular/router';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private router: Router) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: HttpErrorResponse) => {
if (err.status === 401) {
// 401未授权:跳转到登录页
this.router.navigate(['/login']);
} else if (err.status === 404) {
// 404资源不存在:提示用户
alert('请求的资源不存在');
}
// 抛出错误供组件捕获(可选)
return throwError(() => err);
})
);
}
}
加载状态拦截器:全局管理请求加载状态
// loading.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpInterceptor } from '@angular/common/http';
import { finalize } from 'rxjs';
import { LoadingService } from './loading.service';
@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
private requestCount = 0;
constructor(private loadingService: LoadingService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// 开始请求:显示加载状态
if (this.requestCount === 0) {
this.loadingService.showLoading();
}
this.requestCount++;
// 请求完成:隐藏加载状态
return next.handle(req).pipe(
finalize(() => {
this.requestCount--;
if (this.requestCount === 0) {
this.loadingService.hideLoading();
}
})
);
}
}
3.2 错误处理:精细化捕获与处理
HTTP 请求可能出现多种错误(网络错误、状态码错误、响应格式错误),需结合 RxJS 的catchError操作符精细化处理。
组件级错误处理
import { Component, inject, signal } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, of } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-error-demo',
standalone: true,
template: `
@if (error()) {
<div class="error">{{ error() }}</div>
} @else {
@for (item of data(); track item.id) {
<div>{{ item.name }}</div>
}
}
`,
styles: [`.error { color: red; }`]
})
export class ErrorDemoComponent {
private http = inject(HttpClient);
error = signal<string | null>(null);
// 带错误处理的请求
data = toSignal(
this.http.get<any[]>('/api/data').pipe(
catchError((err: HttpErrorResponse) => {
// 区分错误类型
if (err.error instanceof ErrorEvent) {
// 网络错误
this.error.set('网络连接失败,请检查网络');
} else {
// 服务器错误(状态码)
this.error.set(`服务器错误:${err.status} ${err.statusText}`);
}
// 返回空数组避免模板报错
return of([]);
})
),
{ initialValue: [] }
);
}
3.3 请求优化:取消与防抖节流
取消请求(避免内存泄漏)
使用takeUntil操作符在组件销毁时取消未完成的请求:
import { Component, inject, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { takeUntil } from 'rxjs';
import { Subject } from 'rxjs';
@Component({ selector: 'app-cancel-demo', standalone: true, template: '' })
export class CancelDemoComponent implements OnDestroy {
private http = inject(HttpClient);
private destroy$ = new Subject<void>(); // 销毁信号
fetchData() {
this.http.get('/api/long-request')
.pipe(takeUntil(this.destroy$)) // 组件销毁时取消请求
.subscribe(data => console.log(data));
}
// 组件销毁时发送销毁信号
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
防抖节流(减少重复请求)
结合 RxJS 的debounceTime(防抖)或throttleTime(节流)优化高频请求(如搜索框):
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-search',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<input [formControl]="searchControl" placeholder="搜索...">
@for (result of results(); track result.id) {
<div>{{ result.name }}</div>
}
`
})
export class SearchComponent {
private http = inject(HttpClient);
searchControl = new FormControl('');
// 防抖搜索:输入停止300ms后发送请求
results = toSignal(
this.searchControl.valueChanges.pipe(
debounceTime(300), // 防抖300ms
distinctUntilChanged(), // 避免重复输入相同内容
switchMap(keyword => {
// 取消前一次未完成的请求
return this.http.get<any[]>(`/api/search?q=${keyword || ''}`);
})
),
{ initialValue: [] }
);
}
4. 实战案例:用户管理 CRUD 接口封装
将 HTTP 请求封装为服务,实现业务逻辑与组件解耦:
4.1 服务封装(user.service.ts)
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from './user.model';
@Injectable({ providedIn: 'root' })
export class UserService {
private apiUrl = '/api/users';
constructor(private http: HttpClient) {}
// 获取用户列表(支持分页)
getUsers(page = 1, limit = 10): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl, {
params: { page: page.toString(), limit: limit.toString() }
});
}
// 获取单个用户
getUserById(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`);
}
// 创建用户
createUser(user: Omit<User, 'id'>): Observable<User> {
return this.http.post<User>(this.apiUrl, user);
}
// 更新用户
updateUser(id: number, user: Partial<User>): Observable<User> {
return this.http.put<User>(`${this.apiUrl}/${id}`, user);
}
// 删除用户
deleteUser(id: number): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}
}
4.2 组件中使用服务
// user-management.component.ts
import { Component, inject, signal } from '@angular/core';
import { UserService } from './user.service';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-user-management',
standalone: true,
template: `
<button (click)="loadUsers()">加载用户</button>
@for (user of users(); track user.id) {
<div>
{{ user.name }}
<button (click)="deleteUser(user.id)">删除</button>
</div>
}
`
})
export class UserManagementComponent {
private userService = inject(UserService);
users = signal<User[]>([]);
loadUsers() {
this.userService.getUsers().subscribe(users => this.users.set(users));
}
deleteUser(id: number) {
this.userService.deleteUser(id).subscribe(() => {
// 删除后更新列表
this.users.set(this.users().filter(user => user.id !== id));
});
}
}
5. v20 HTTP 服务优化亮点
- 树摇友好:
provideHttpClient按需加载特性,未使用的 HTTP 功能(如拦截器)可被树摇移除,减少 bundle 体积 - 无 Zone.js 兼容:HTTP 服务完全支持无 Zone.js 架构,通过 Signals 触发变更检测,避免 Zone.js 的性能开销
- RxJS 联动增强:
toSignal简化 Observable 到 Signal 的转换,响应式数据更新更高效 - 类型安全强化:请求 / 响应类型推断更精准,结合 TypeScript 减少类型错误

浙公网安备 33010602011771号