Angular提供了强大的HttpClient模块来处理HTTP请求,支持RESTful API调用、请求拦截、响应处理等功能。
HttpClientModule
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; // 导入HttpClientModule
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule // 添加到imports数组
],
bootstrap: [AppComponent]
})
export class AppModule { }
最佳实践是将HTTP请求封装在服务中:
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry, map } from 'rxjs/operators';
export interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
@Injectable({
providedIn: 'root' // 根注入,单例模式
})
export class UserService {
private apiUrl = 'https://api.example.com/users';
constructor(private http: HttpClient) { }
// GET请求 - 获取所有用户
getUsers(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl);
}
// GET请求 - 获取单个用户
getUser(id: number): Observable<User> {
const url = `${this.apiUrl}/${id}`;
return this.http.get<User>(url);
}
// POST请求 - 创建用户
createUser(user: User): Observable<User> {
return this.http.post<User>(this.apiUrl, user, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
// PUT请求 - 更新用户
updateUser(id: number, user: User): Observable<User> {
const url = `${this.apiUrl}/${id}`;
return this.http.put<User>(url, user);
}
// DELETE请求 - 删除用户
deleteUser(id: number): Observable<void> {
const url = `${this.apiUrl}/${id}`;
return this.http.delete<void>(url);
}
// 带查询参数的GET请求
searchUsers(term: string): Observable<User[]> {
const params = new HttpParams()
.set('q', term)
.set('_limit', '10');
return this.http.get<User[]>(this.apiUrl, { params });
}
}
// user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService, User } from './user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
users: User[] = [];
loading = false;
error = '';
constructor(private userService: UserService) { }
ngOnInit(): void {
this.loadUsers();
}
loadUsers(): void {
this.loading = true;
this.userService.getUsers().subscribe({
next: (data) => {
this.users = data;
this.loading = false;
},
error: (err) => {
this.error = '加载用户数据失败: ' + err.message;
this.loading = false;
},
complete: () => {
console.log('用户数据加载完成');
}
});
}
createUser(): void {
const newUser: User = {
id: 0, // 服务器端会生成ID
name: '新用户',
email: 'newuser@example.com',
createdAt: new Date()
};
this.userService.createUser(newUser).subscribe({
next: (createdUser) => {
this.users.push(createdUser);
alert('用户创建成功');
},
error: (err) => {
console.error('创建用户失败:', err);
alert('创建用户失败');
}
});
}
deleteUser(id: number): void {
if (confirm('确定要删除这个用户吗?')) {
this.userService.deleteUser(id).subscribe({
next: () => {
this.users = this.users.filter(user => user.id !== id);
},
error: (err) => {
console.error('删除用户失败:', err);
}
});
}
}
}
<!-- user-list.component.html -->
<div class="user-list">
<h2>用户列表</h2>
<div *ngIf="loading" class="loading">
<i class="fas fa-spinner fa-spin"></i> 加载中...
</div>
<div *ngIf="error" class="alert alert-danger">
{{error}}
</div>
<button (click)="createUser()" class="btn btn-primary mb-3">
创建新用户
</button>
<table class="table table-striped" *ngIf="users.length > 0">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
<td>{{user.createdAt | date}}</td>
<td>
<button (click)="deleteUser(user.id)" class="btn btn-danger btn-sm">
删除
</button>
</td>
</tr>
</tbody>
</table>
<div *ngIf="users.length === 0 && !loading" class="alert alert-info">
暂无用户数据
</div>
</div>
使用RxJS操作符进行错误处理:
// 在服务中添加错误处理
export class UserService {
private handleError(error: any): Observable<never> {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// 客户端错误
errorMessage = `客户端错误: ${error.error.message}`;
} else {
// 服务器端错误
errorMessage = `服务器错误: ${error.status} - ${error.statusText || ''} ${error.error || ''}`;
}
console.error(errorMessage);
return throwError(() => new Error(errorMessage));
}
// 带错误处理的GET请求
getUsersSafe(): Observable<User[]> {
return this.http.get<User[]>(this.apiUrl).pipe(
retry(2), // 失败时重试2次
catchError(this.handleError)
);
}
// 处理特定状态码
getUserWithStatusHandling(id: number): Observable<User> {
return this.http.get<User>(`${this.apiUrl}/${id}`, {
observe: 'response' // 获取完整响应对象
}).pipe(
map(response => {
if (response.status === 200) {
return response.body!;
} else if (response.status === 404) {
throw new Error('用户不存在');
} else {
throw new Error(`请求失败,状态码: ${response.status}`);
}
}),
catchError(this.handleError)
);
}
}
拦截器用于在请求发出前或响应返回前进行统一处理:
// auth.interceptor.ts - 认证拦截器
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private router: Router) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// 获取token
const token = localStorage.getItem('auth_token');
// 克隆请求并添加认证头
let authRequest = request;
if (token) {
authRequest = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
// 添加公共请求头
authRequest = authRequest.clone({
setHeaders: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
return next.handle(authRequest).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// 未授权,跳转到登录页
this.router.navigate(['/login']);
} else if (error.status === 403) {
// 禁止访问
alert('权限不足,无法访问该资源');
} else if (error.status === 0) {
// 网络错误
console.error('网络连接错误,请检查网络设置');
}
return throwError(() => error);
})
);
}
}
// logging.interceptor.ts - 日志拦截器
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpResponse
} from '@angular/common/http';
import { Observable, tap } from 'rxjs';
import { environment } from '../environments/environment';
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const startTime = Date.now();
// 只在开发环境记录日志
if (!environment.production) {
console.log(`请求开始: ${request.method} ${request.url}`);
}
return next.handle(request).pipe(
tap(
event => {
if (event instanceof HttpResponse) {
const elapsed = Date.now() - startTime;
if (!environment.production) {
console.log(`请求完成: ${request.method} ${request.url} (${elapsed}ms)`);
}
}
},
error => {
const elapsed = Date.now() - startTime;
console.error(`请求失败: ${request.method} ${request.url} (${elapsed}ms)`, error);
}
)
);
}
}
// 注册拦截器
// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './interceptors/auth.interceptor';
import { LoggingInterceptor } from './interceptors/logging.interceptor';
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: LoggingInterceptor,
multi: true
}
]
})
export class AppModule { }
// environments/environment.ts - 开发环境
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api',
apiKey: 'dev-key-123',
version: '1.0.0'
};
// environments/environment.prod.ts - 生产环境
export const environment = {
production: true,
apiUrl: 'https://api.production.com/api',
apiKey: 'prod-key-456',
version: '1.0.0'
};
// 在服务中使用环境变量
import { environment } from '../environments/environment';
export class UserService {
private apiUrl = `${environment.apiUrl}/users`;
constructor(private http: HttpClient) { }
}
// api.service.ts - 通用API服务
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map, timeout, retry } from 'rxjs/operators';
import { environment } from '../environments/environment';
export interface ApiResponse<T> {
data: T;
status: number;
message: string;
timestamp: Date;
}
@Injectable({
providedIn: 'root'
})
export class ApiService {
private readonly baseUrl = environment.apiUrl;
private readonly defaultTimeout = 10000; // 10秒超时
constructor(private http: HttpClient) { }
// 通用GET方法
get<T>(endpoint: string, params?: any): Observable<ApiResponse<T>> {
let httpParams = new HttpParams();
if (params) {
Object.keys(params).forEach(key => {
if (params[key] !== null && params[key] !== undefined) {
httpParams = httpParams.set(key, params[key].toString());
}
});
}
return this.http.get<ApiResponse<T>>(`${this.baseUrl}/${endpoint}`, {
params: httpParams
}).pipe(
timeout(this.defaultTimeout),
retry(1),
catchError(this.handleError)
);
}
// 通用POST方法
post<T>(endpoint: string, body: any): Observable<ApiResponse<T>> {
return this.http.post<ApiResponse<T>>(
`${this.baseUrl}/${endpoint}`,
body,
{
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}
).pipe(
timeout(this.defaultTimeout),
catchError(this.handleError)
);
}
// 通用PUT方法
put<T>(endpoint: string, body: any): Observable<ApiResponse<T>> {
return this.http.put<ApiResponse<T>>(
`${this.baseUrl}/${endpoint}`,
body
).pipe(
timeout(this.defaultTimeout),
catchError(this.handleError)
);
}
// 通用DELETE方法
delete<T>(endpoint: string): Observable<ApiResponse<T>> {
return this.http.delete<ApiResponse<T>>(
`${this.baseUrl}/${endpoint}`
).pipe(
timeout(this.defaultTimeout),
catchError(this.handleError)
);
}
// 文件上传
uploadFile<T>(endpoint: string, file: File): Observable<ApiResponse<T>> {
const formData = new FormData();
formData.append('file', file);
return this.http.post<ApiResponse<T>>(
`${this.baseUrl}/${endpoint}`,
formData
).pipe(
catchError(this.handleError)
);
}
// 错误处理
private handleError(error: any): Observable<never> {
console.error('API调用错误:', error);
let userMessage = '请求失败,请稍后重试';
if (error.status === 0) {
userMessage = '网络连接失败,请检查网络设置';
} else if (error.status === 401) {
userMessage = '登录已过期,请重新登录';
} else if (error.status === 404) {
userMessage = '请求的资源不存在';
} else if (error.status >= 500) {
userMessage = '服务器错误,请稍后重试';
}
return throwError(() => ({
message: userMessage,
originalError: error
}));
}
}
如果遇到跨域问题,需要在服务器端配置CORS头,或在开发环境中配置代理:
// proxy.conf.json
{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
}
}
在angular.json中配置代理:
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "proxy.conf.json"
}
}