MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

掌握Angular HttpClient模块的配置与使用

2022-01-177.2k 阅读

Angular HttpClient 模块基础

在 Angular 开发中,HttpClient 模块是与后端服务器进行数据交互的重要工具。它提供了简洁且强大的 API 来处理 HTTP 请求和响应。要使用 HttpClient,首先需要在项目中引入相关模块。

在 Angular 项目的根模块(通常是 app.module.ts)中,导入 HttpClientModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';

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

上述代码中,通过 import { HttpClientModule } from '@angular/common/http'; 引入了 HttpClientModule,并将其添加到 imports 数组中。这样,整个应用程序就可以使用 HttpClient 相关功能了。

简单的 GET 请求

使用 HttpClient 发送 GET 请求是最常见的操作之一。假设后端有一个 API 接口 /api/users,用于获取用户列表。在 Angular 服务中可以这样实现:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any> {
    return this.http.get(this.apiUrl);
  }
}

在上述代码中,我们创建了一个 UserService 服务,并在构造函数中注入了 HttpClientgetUsers 方法使用 http.get 方法发送 GET 请求到 apiUrl。这里返回的是一个 Observable 对象,这是 RxJS(Reactive Extensions for JavaScript)中的核心概念。Observable 代表一个可观察的数据流,Angular 使用它来处理异步操作,如 HTTP 请求。

在组件中使用这个服务获取用户列表:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - user - list',
  templateUrl: './user - list.component.html',
  styleUrls: ['./user - list.component.css']
})
export class UserListComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getUsers().subscribe((data) => {
      this.users = data;
    });
  }
}

UserListComponentngOnInit 生命周期钩子中,调用 userService.getUsers() 方法,并通过 subscribe 订阅 Observable。当服务器响应回来时,subscribe 中的回调函数会被执行,将响应数据赋值给 users 数组,从而在组件模板中可以显示用户列表。

GET 请求带参数

有时候,我们需要在 GET 请求中带上参数。例如,后端 API /api/users 支持根据年龄过滤用户,我们可以这样发送带参数的 GET 请求:

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsersByAge(age: number): Observable<any> {
    let params = new HttpParams();
    params = params.append('age', age.toString());
    return this.http.get(this.apiUrl, { params });
  }
}

getUsersByAge 方法中,我们创建了一个 HttpParams 对象,并使用 append 方法添加了一个名为 age 的参数。然后将这个 params 对象作为第二个参数传递给 http.get 方法。这样,请求会以 /api/users?age=25 的形式发送到后端(假设 age 为 25)。

在组件中使用这个方法:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - user - list - by - age',
  templateUrl: './user - list - by - age.component.html',
  styleUrls: ['./user - list - by - age.component.css']
})
export class UserListByAgeComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getUsersByAge(25).subscribe((data) => {
      this.users = data;
    });
  }
}

POST 请求

POST 请求通常用于向服务器提交数据,比如创建新用户。假设后端有一个 /api/users 的 API 用于创建用户,请求体包含用户的姓名和年龄。在 Angular 服务中实现如下:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  createUser(user: { name: string; age: number }): Observable<any> {
    return this.http.post(this.apiUrl, user);
  }
}

createUser 方法中,我们将用户数据作为第二个参数传递给 http.post 方法。第一个参数是 API 的 URL。

在组件中使用这个服务创建用户:

import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - create - user',
  templateUrl: './create - user.component.html',
  styleUrls: ['./create - user.component.css']
})
export class CreateUserComponent {
  newUser = { name: '', age: 0 };

  constructor(private userService: UserService) { }

  onSubmit() {
    this.userService.createUser(this.newUser).subscribe((response) => {
      console.log('User created successfully:', response);
    });
  }
}

CreateUserComponent 中,我们定义了一个 newUser 对象来存储用户输入的数据。当用户点击提交按钮(在模板中通过 (submit) 事件绑定到 onSubmit 方法),调用 userService.createUser 方法,并在 subscribe 中处理服务器的响应。

PUT 请求

PUT 请求一般用于更新服务器上的资源。假设我们有一个 /api/users/:id 的 API,用于更新指定 ID 的用户信息。在 Angular 服务中实现如下:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  updateUser(id: number, user: { name: string; age: number }): Observable<any> {
    const url = `${this.apiUrl}/${id}`;
    return this.http.put(url, user);
  }
}

updateUser 方法中,我们构建了包含用户 ID 的 URL,并将用户数据作为第二个参数传递给 http.put 方法。

在组件中使用这个服务更新用户:

import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - update - user',
  templateUrl: './update - user.component.html',
  styleUrls: ['./update - user.component.css']
})
export class UpdateUserComponent {
  userId = 1;
  updatedUser = { name: '', age: 0 };

  constructor(private userService: UserService) { }

  onSubmit() {
    this.userService.updateUser(this.userId, this.updatedUser).subscribe((response) => {
      console.log('User updated successfully:', response);
    });
  }
}

DELETE 请求

DELETE 请求用于从服务器删除资源。假设我们有一个 /api/users/:id 的 API,用于删除指定 ID 的用户。在 Angular 服务中实现如下:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  deleteUser(id: number): Observable<any> {
    const url = `${this.apiUrl}/${id}`;
    return this.http.delete(url);
  }
}

deleteUser 方法中,我们构建了包含用户 ID 的 URL,并调用 http.delete 方法。

在组件中使用这个服务删除用户:

import { Component } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - delete - user',
  templateUrl: './delete - user.component.html',
  styleUrls: ['./delete - user.component.css']
})
export class DeleteUserComponent {
  userId = 1;

  constructor(private userService: UserService) { }

  onDelete() {
    this.userService.deleteUser(this.userId).subscribe((response) => {
      console.log('User deleted successfully:', response);
    });
  }
}

处理 HTTP 响应

HttpClient 返回的 Observable 会发出一个代表 HTTP 响应的对象。这个对象包含了响应的状态码、头信息以及响应体等内容。默认情况下,HttpClient 会将响应体解析为 JSON 格式(如果响应的 Content - Typeapplication/json)。

我们可以通过 map 操作符(来自 RxJS)对响应进行进一步处理。例如,假设我们的服务器返回的用户数据包含一个额外的包装层,我们需要提取实际的用户列表:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any[]> {
    return this.http.get(this.apiUrl).pipe(
      map((response: any) => {
        return response.data;
      })
    );
  }
}

在上述代码中,pipe 方法用于组合 RxJS 操作符。map 操作符会对 Observable 发出的每个值(这里是 HTTP 响应)进行转换。我们从响应中提取 data 字段并返回,这样在组件中订阅时,得到的就是实际的用户列表。

处理错误

在进行 HTTP 请求时,可能会发生各种错误,如网络问题、服务器错误等。HttpClient 会将错误作为 Observableerror 通知发出。我们可以通过 catchError 操作符(来自 RxJS)来处理这些错误。

在服务中处理错误示例:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any[]> {
    return this.http.get(this.apiUrl).pipe(
      catchError((error) => {
        console.error('Error fetching users:', error);
        return throwError('An error occurred while fetching users.');
      })
    );
  }
}

catchError 回调函数中,我们首先在控制台打印错误信息,然后使用 throwError 返回一个新的 Observable,发出错误消息。在组件中订阅时,可以捕获这个错误:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - user - list',
  templateUrl: './user - list.component.html',
  styleUrls: ['./user - list.component.css']
})
export class UserListComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getUsers().subscribe(
      (data) => {
        this.users = data;
      },
      (error) => {
        console.error('Component caught error:', error);
      }
    );
  }
}

设置请求头

有时候,我们需要在请求中设置自定义的请求头。例如,后端 API 要求在请求头中包含认证令牌。在 HttpClient 中可以这样设置:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any> {
    const headers = new HttpHeaders().set('Authorization', 'Bearer your - token - here');
    return this.http.get(this.apiUrl, { headers });
  }
}

在上述代码中,我们创建了一个 HttpHeaders 对象,并使用 set 方法设置了 Authorization 头。然后将这个 headers 对象作为第二个参数传递给 http.get 方法。

如果需要设置多个头信息,可以链式调用 set 方法:

const headers = new HttpHeaders()
  .set('Authorization', 'Bearer your - token - here')
  .set('Content - Type', 'application/json');

拦截器的使用

Angular 的 HTTP 拦截器提供了一种强大的机制,可以在请求发送前和响应接收后对其进行全局处理。例如,我们可以使用拦截器统一添加认证头,或者处理所有请求的错误。

首先,创建一个拦截器类,实现 HttpInterceptor 接口:

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }
    return next.handle(request);
  }
}

intercept 方法中,我们首先从本地存储中获取认证令牌。如果令牌存在,使用 clone 方法创建一个新的 HttpRequest,并设置 Authorization 头。然后将新的请求传递给 next.handle,继续处理请求。

要使拦截器生效,需要在模块的 providers 中注册它:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform - browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { AuthInterceptor } from './auth.interceptor';

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

providers 数组中,我们使用 provide: HTTP_INTERCEPTORS 来注册拦截器。useClass 指定拦截器类,multi: true 表示可以有多个拦截器。

处理复杂的响应类型

有时候,服务器返回的响应可能不是简单的 JSON 格式,或者需要更复杂的解析。例如,服务器返回的是 XML 格式的数据。我们可以使用 HttpClientresponseType 选项来处理这种情况。

假设后端有一个 /api/report 的 API 返回 XML 格式的报告数据。在服务中可以这样处理:

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

@Injectable({
  providedIn: 'root'
})
export class ReportService {
  private apiUrl = '/api/report';

  constructor(private http: HttpClient) { }

  getReport(): Observable<Document> {
    return this.http.get(this.apiUrl, { responseType: 'text' }).pipe(
      map((text) => {
        const parser = new DOMParser();
        return parser.parseFromString(text, 'application/xml');
      })
    );
  }
}

getReport 方法中,我们设置 responseTypetext,因为 HttpClient 默认不支持直接解析 XML。然后使用 map 操作符将返回的文本解析为 XML Document 对象。

在组件中使用这个服务获取报告:

import { Component, OnInit } from '@angular/core';
import { ReportService } from './report.service';

@Component({
  selector: 'app - report',
  templateUrl: './report.component.html',
  styleUrls: ['./report.component.css']
})
export class ReportComponent implements OnInit {
  report: Document | null = null;

  constructor(private reportService: ReportService) { }

  ngOnInit(): void {
    this.reportService.getReport().subscribe((data) => {
      this.report = data;
    });
  }
}

并发请求

在实际应用中,可能会遇到需要同时发起多个 HTTP 请求的情况。RxJS 提供了一些操作符来处理并发请求,如 forkJoin

假设我们有两个服务,一个获取用户列表,另一个获取产品列表。我们想同时发起这两个请求,并在两个请求都完成后处理结果。

首先,创建两个服务:

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

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any[]> {
    return this.http.get(this.apiUrl);
  }
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ProductService {
  private apiUrl = '/api/products';

  constructor(private http: HttpClient) { }

  getProducts(): Observable<any[]> {
    return this.http.get(this.apiUrl);
  }
}

在组件中使用 forkJoin 处理并发请求:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
import { ProductService } from './product.service';
import { forkJoin } from 'rxjs';

@Component({
  selector: 'app - combined - data',
  templateUrl: './combined - data.component.html',
  styleUrls: ['./combined - data.component.css']
})
export class CombinedDataComponent implements OnInit {
  users: any[] = [];
  products: any[] = [];

  constructor(private userService: UserService, private productService: ProductService) { }

  ngOnInit(): void {
    forkJoin([
      this.userService.getUsers(),
      this.productService.getProducts()
    ]).subscribe(([usersData, productsData]) => {
      this.users = usersData;
      this.products = productsData;
    });
  }
}

ngOnInit 中,我们使用 forkJoin 传入两个 Observable(分别来自 userService.getUsersproductService.getProducts)。forkJoin 会等待所有传入的 Observable 都完成,并将它们的结果作为数组发出。在 subscribe 回调中,我们解构这个数组,分别获取用户数据和产品数据。

缓存 HTTP 响应

在某些情况下,我们可能希望缓存 HTTP 响应,以避免重复请求相同的数据。可以通过结合 RxJS 的 shareReplay 操作符来实现简单的缓存机制。

在服务中缓存用户列表请求:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = '/api/users';
  private userCache$: Observable<any[]> | null = null;

  constructor(private http: HttpClient) { }

  getUsers(): Observable<any[]> {
    if (!this.userCache$) {
      this.userCache$ = this.http.get(this.apiUrl).pipe(
        shareReplay(1)
      );
    }
    return this.userCache$;
  }
}

getUsers 方法中,我们首先检查 userCache$ 是否已经存在。如果不存在,发送 HTTP 请求并使用 shareReplay(1) 操作符。shareReplay(1) 会缓存最新的一个值,并将其重新发送给新的订阅者,这样后续的订阅者不会再次触发 HTTP 请求。

在组件中使用这个服务获取用户列表:

import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app - user - list',
  templateUrl: './user - list.component.html',
  styleUrls: ['./user - list.component.css']
})
export class UserListComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getUsers().subscribe((data) => {
      this.users = data;
    });
  }
}

当这个组件首次订阅 userService.getUsers() 时,会发起 HTTP 请求并缓存响应。如果其他组件也订阅 getUsers(),则会直接使用缓存的数据,而不会再次发起请求。

与 GraphQL 的集成

GraphQL 是一种用于 API 的查询语言,越来越受到开发者的青睐。在 Angular 项目中,可以结合 HttpClient 与 GraphQL 进行数据交互。

首先,安装相关依赖,如 graphql - client

npm install graphql - client

假设我们有一个 GraphQL 服务器,端点为 /graphql。创建一个服务来执行 GraphQL 查询:

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

@Injectable({
  providedIn: 'root'
})
export class GraphQLService {
  private graphqlUrl = '/graphql';

  constructor(private http: HttpClient) { }

  query<T>(query: string, variables?: any): Observable<T> {
    const body = { query, variables };
    return this.http.post<T>(this.graphqlUrl, body);
  }
}

query 方法中,我们构建了包含查询语句和变量(如果有)的请求体,并使用 http.post 发送到 GraphQL 端点。

假设我们有一个简单的 GraphQL 查询,用于获取用户列表:

query GetUsers {
  users {
    id
    name
    age
  }
}

在组件中使用这个服务执行查询:

import { Component, OnInit } from '@angular/core';
import { GraphQLService } from './graphql.service';

@Component({
  selector: 'app - graphql - user - list',
  templateUrl: './graphql - user - list.component.html',
  styleUrls: ['./graphql - user - list.component.css']
})
export class GraphQLUserListComponent implements OnInit {
  users: any[] = [];
  private query = `
    query GetUsers {
      users {
        id
        name
        age
      }
    }
  `;

  constructor(private graphQLService: GraphQLService) { }

  ngOnInit(): void {
    this.graphQLService.query<any[]>(this.query).subscribe((data) => {
      this.users = data.users;
    });
  }
}

ngOnInit 中,我们调用 graphQLService.query 方法,传入查询语句,并在 subscribe 中处理响应数据。这样就实现了在 Angular 中使用 HttpClient 与 GraphQL 进行数据交互。

通过以上对 Angular HttpClient 模块的详细介绍,从基础的请求方法到复杂的功能实现,相信开发者能够全面掌握并灵活运用该模块,构建出高效、健壮的前端应用与后端服务器进行数据交互。无论是处理简单的 RESTful API 还是新兴的 GraphQL,HttpClient 都提供了强大的支持,结合 RxJS 的丰富操作符,能满足各种复杂的业务需求。在实际项目中,还需要根据具体场景,合理运用这些知识,优化应用性能,提升用户体验。