HttpClient基础 #

一、HttpClient概述 #

HttpClient是Angular提供的HTTP客户端,用于与后端API进行通信。

text
┌─────────────────────────────────────────────────────┐
│                  Angular 应用                        │
├─────────────────────────────────────────────────────┤
│  ┌─────────────────────────────────────────────┐   │
│  │              HttpClient                      │   │
│  │  GET | POST | PUT | DELETE | PATCH          │   │
│  └─────────────────────────────────────────────┘   │
│                        ↓                            │
│  ┌─────────────────────────────────────────────┐   │
│  │              HTTP拦截器                      │   │
│  │  认证 | 日志 | 错误处理                      │   │
│  └─────────────────────────────────────────────┘   │
│                        ↓                            │
│  ┌─────────────────────────────────────────────┐   │
│  │              后端 API                        │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘

二、配置HttpClient #

2.1 导入HttpClient #

typescript
import { provideHttpClient } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient()
  ]
};

2.2 注入HttpClient #

typescript
import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}
}

三、GET请求 #

3.1 基本GET请求 #

typescript
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UserService {
  private apiUrl = 'https://api.example.com/users';
  
  constructor(private http: HttpClient) {}
  
  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }
  
  getUser(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }
}

3.2 带查询参数 #

typescript
import { HttpParams } from '@angular/common/http';

getUsers(params: { page?: number; size?: number; name?: string }): Observable<User[]> {
  let httpParams = new HttpParams();
  
  if (params.page) {
    httpParams = httpParams.set('page', params.page.toString());
  }
  if (params.size) {
    httpParams = httpParams.set('size', params.size.toString());
  }
  if (params.name) {
    httpParams = httpParams.set('name', params.name);
  }
  
  return this.http.get<User[]>(this.apiUrl, { params: httpParams });
}

// 或使用fromObject
getUsers2(params: any): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl, {
    params: {
      page: params.page || 1,
      size: params.size || 10,
      name: params.name || ''
    }
  });
}

3.3 带请求头 #

typescript
import { HttpHeaders } from '@angular/common/http';

getUserWithHeaders(id: number): Observable<User> {
  const headers = new HttpHeaders({
    'Authorization': 'Bearer token123',
    'Content-Type': 'application/json'
  });
  
  return this.http.get<User>(`${this.apiUrl}/${id}`, { headers });
}

3.4 完整请求选项 #

typescript
getUsersWithOptions(): Observable<HttpResponse<User[]>> {
  return this.http.get<User[]>(this.apiUrl, {
    params: { page: '1', size: '10' },
    headers: new HttpHeaders({
      'Authorization': 'Bearer token'
    }),
    observe: 'response',
    responseType: 'json'
  });
}

四、POST请求 #

4.1 基本POST请求 #

typescript
createUser(user: User): Observable<User> {
  return this.http.post<User>(this.apiUrl, user);
}

4.2 带请求头 #

typescript
createUserWithHeaders(user: User): Observable<User> {
  const headers = new HttpHeaders({
    'Authorization': 'Bearer token',
    'Content-Type': 'application/json'
  });
  
  return this.http.post<User>(this.apiUrl, user, { headers });
}

4.3 表单数据 #

typescript
import { HttpParams } from '@angular/common/http';

login(credentials: LoginCredentials): Observable<AuthResponse> {
  const body = new HttpParams()
    .set('username', credentials.username)
    .set('password', credentials.password);
  
  return this.http.post<AuthResponse>(`${this.apiUrl}/login`, body, {
    headers: new HttpHeaders({
      'Content-Type': 'application/x-www-form-urlencoded'
    })
  });
}

4.4 FormData上传 #

typescript
uploadFile(file: File): Observable<UploadResponse> {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('description', '文件描述');
  
  return this.http.post<UploadResponse>(`${this.apiUrl}/upload`, formData);
}

五、PUT请求 #

typescript
updateUser(id: number, user: User): Observable<User> {
  return this.http.put<User>(`${this.apiUrl}/${id}`, user);
}

updateUserPartial(id: number, changes: Partial<User>): Observable<User> {
  return this.http.patch<User>(`${this.apiUrl}/${id}`, changes);
}

六、DELETE请求 #

typescript
deleteUser(id: number): Observable<void> {
  return this.http.delete<void>(`${this.apiUrl}/${id}`);
}

deleteUserWithResponse(id: number): Observable<HttpResponse<any>> {
  return this.http.delete(`${this.apiUrl}/${id}`, {
    observe: 'response'
  });
}

七、错误处理 #

7.1 基本错误处理 #

typescript
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

getUsers(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    catchError(this.handleError)
  );
}

private handleError(error: HttpErrorResponse) {
  let errorMessage = '发生未知错误';
  
  if (error.error instanceof ErrorEvent) {
    errorMessage = `客户端错误: ${error.error.message}`;
  } else {
    switch (error.status) {
      case 400:
        errorMessage = '请求参数错误';
        break;
      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));
}

7.2 服务中统一处理 #

typescript
@Injectable({ providedIn: 'root' })
export class ApiService {
  private baseUrl = 'https://api.example.com';
  
  constructor(private http: HttpClient) {}
  
  get<T>(url: string, params?: any): Observable<T> {
    return this.http.get<T>(`${this.baseUrl}${url}`, { params }).pipe(
      retry(3),
      catchError(this.handleError)
    );
  }
  
  post<T>(url: string, body: any): Observable<T> {
    return this.http.post<T>(`${this.baseUrl}${url}`, body).pipe(
      catchError(this.handleError)
    );
  }
  
  put<T>(url: string, body: any): Observable<T> {
    return this.http.put<T>(`${this.baseUrl}${url}`, body).pipe(
      catchError(this.handleError)
    );
  }
  
  delete<T>(url: string): Observable<T> {
    return this.http.delete<T>(`${this.baseUrl}${url}`).pipe(
      catchError(this.handleError)
    );
  }
  
  private handleError(error: HttpErrorResponse) {
    return throwError(() => error);
  }
}

八、请求重试 #

8.1 使用retry #

typescript
import { retry, retryWhen, delay, scan } from 'rxjs/operators';

getUsers(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    retry(3)
  );
}

// 指数退避重试
getUsersWithBackoff(): Observable<User[]> {
  return this.http.get<User[]>(this.apiUrl).pipe(
    retryWhen(errors =>
      errors.pipe(
        scan((acc, error) => {
          if (acc >= 3) {
            throw error;
          }
          return acc + 1;
        }, 0),
        delay(1000)
      )
    )
  );
}

九、完整服务示例 #

typescript
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry, map } from 'rxjs/operators';

interface User {
  id: number;
  name: string;
  email: string;
}

interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  size: number;
}

@Injectable({ providedIn: 'root' })
export class UserService {
  private apiUrl = 'https://api.example.com/users';
  
  constructor(private http: HttpClient) {}
  
  getUsers(page = 1, size = 10, name?: string): Observable<PaginatedResponse<User>> {
    let params = new HttpParams()
      .set('page', page.toString())
      .set('size', size.toString());
    
    if (name) {
      params = params.set('name', name);
    }
    
    return this.http.get<PaginatedResponse<User>>(this.apiUrl, { params }).pipe(
      retry(2),
      catchError(this.handleError)
    );
  }
  
  getUser(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`).pipe(
      catchError(this.handleError)
    );
  }
  
  createUser(user: Omit<User, 'id'>): Observable<User> {
    return this.http.post<User>(this.apiUrl, user).pipe(
      catchError(this.handleError)
    );
  }
  
  updateUser(id: number, user: Partial<User>): Observable<User> {
    return this.http.put<User>(`${this.apiUrl}/${id}`, user).pipe(
      catchError(this.handleError)
    );
  }
  
  patchUser(id: number, changes: Partial<User>): Observable<User> {
    return this.http.patch<User>(`${this.apiUrl}/${id}`, changes).pipe(
      catchError(this.handleError)
    );
  }
  
  deleteUser(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`).pipe(
      catchError(this.handleError)
    );
  }
  
  searchUsers(query: string): Observable<User[]> {
    return this.http.get<User[]>(`${this.apiUrl}/search`, {
      params: { q: query }
    }).pipe(
      catchError(this.handleError)
    );
  }
  
  private handleError(error: HttpErrorResponse) {
    let errorMessage = '发生未知错误';
    
    if (error.error instanceof ErrorEvent) {
      errorMessage = `客户端错误: ${error.error.message}`;
    } else {
      errorMessage = `服务器错误: ${error.status} - ${error.message}`;
    }
    
    console.error('HTTP错误:', error);
    return throwError(() => new Error(errorMessage));
  }
}

十、在组件中使用 #

typescript
import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
import { finalize } from 'rxjs/operators';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngIf="loading">加载中...</div>
    <div *ngIf="error" class="error">{{ error }}</div>
    
    <ul *ngIf="!loading && !error">
      <li *ngFor="let user of users">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users: User[] = [];
  loading = false;
  error: string | null = null;
  
  constructor(private userService: UserService) {}
  
  ngOnInit() {
    this.loadUsers();
  }
  
  loadUsers() {
    this.loading = true;
    this.error = null;
    
    this.userService.getUsers()
      .pipe(
        finalize(() => this.loading = false)
      )
      .subscribe({
        next: (response) => {
          this.users = response.data;
        },
        error: (err) => {
          this.error = err.message;
        }
      });
  }
}

十一、总结 #

方法 说明
get() GET请求
post() POST请求
put() PUT请求
patch() PATCH请求
delete() DELETE请求
head() HEAD请求
options() OPTIONS请求
request() 通用请求方法

下一步:HTTP拦截器

最后更新:2026-03-26