六、Angular 发送请求/ HttpClient 模块

devtools/2025/1/12 4:16:28/

一、应用 HttpClient 模块

  • @angular/common/http 中的 HttpClient 类基于浏览器提供的 XMLHttpRequest 接口。
  • 要想使用 HtpClient 模块,就要先导入 Anqular 的 HttpClientModule。大多数 Web 应用程序都会在根模块 AppModule 中导入它。
  1. 编辑 src/app/app.module.ts,导入 HttpClientModule 模块,导入顺序要在 BrowserModule 之后

    javascript">import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    //导入 httpClient
    import { HttpClientModule } from '@angular/common/http';import { AppComponent } from './app.component';@NgModule({declarations: [AppComponent],imports: [BrowserModule,HttpClientModule //导入 httpClient,注意要放在 BrowserModule 之后],providers: [],bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  2. 通过构造函数将实例注册到类中

    javascript">import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';@Injectable()
    export class DemoService {constructor(private http: HttpClient) {}
    }
    

二、模拟后端接口(创建RESTful API 服务)

[1]. 使用 json-server 创建 RESTful API 服务

  • json-server 是一个 Node.js 模块,底层运行在 Express 服务器上,用户可以指定一个JSON 文件作为 RESTful API 服务的数据源。 使用json-server 在本地搭建一个JSON 服务器对外提供 RESTful API 服务。前端开发工程师在无后端的情况下,可以用它作为后端 RESTfulAPI 服务器
  1. 全局安装 json-server
    node 版本超过 14 直接安装即可

    npm install -g json-server
    

    我的node版本为 12.11.0,所以选择固定 json-server 版本

    npm install -g json-server@0.17.4
    
  2. 在任意位置创建 data 目录,并创建 db.json 文件,内容如下

    javascript">{"data": [{"username": "张三","age": 15}]
    }
    
  3. 在 data 目录下打开命令行窗口, 输入如下指令启动 json-server

    json-server db.json
    

    控制台将会有如下信息

  4. 在浏览器中输入 http://localhost:3000/data

[2]. 使用 Angular 内存数据库模拟服务器

  • Angular 内存数据库基于in-memory-web-api库,该库用于 Angular 演示和测试时调用内存中的网络 API,可模仿RESTful API 服务上的 CRUD 增、、改、查操作。它拦截了 Angular的 HTTP 请求和HttpClient 请求,这些请求原本会发送到远程服务器,然后将它们重定向到定义的内存数据库中。
  • in-memory-web-api 库集成在 Angular 中,该库会替换 HttpClient 模块中的 HttpBackend服务,新的服务会模拟 RESTful 风格的后端的行为。
  • in-memory-web-api 库仅拦截了 Angular 中的 HTTP 请求,它实际上没有运行 Web服务器。因此我们不能通过浏览器或者其他 Angular 环境外的工具访问它的 RESTful API 资源。
  • in-memory-web-api 库所虚拟的 API位于内存中,这也就意味着当刷新浏览器后,所有的数据都会消失。
  • 使用 Angular 内存数据库的优势显而易见: 无须单独构建和启动测试服务器
  1. 在angualr 项目中安装 in-memory-web-api 库
    我的node版本为 12.11.0,所以选择固定 angular-in-memory-web-api 版本

    javascript">npm i angular-in-memory-web-api@0.10.0 --save
    
  2. src/app 目录下新建 In-mem-hero-service.ts 文件,内容如下

    javascript">import { InMemoryDbService } from 'angular-in-memory-web-api';export class InMemHeroService implements InMemoryDbService {// 创建模拟数据createDb() {// 变量名 heroes 将被视作 URL 的一部分let heroes = [{ id: 1, name: '张三' },{ id: 2, name: '李四' },{ id: 3, name: '王五' },{ id: 4, name: '赵六' },{ id: 5, name: '孙琦' }];return { heroes };}
    }
    
  3. src/app/app.module.ts 文件中导入 InMemHeroService 类

    • HttpClientInMemoryWebApiModule 的 forRoot 方法的可以提供第二个参数
      HttpclientInMemoryWebApiModule.forRoot(InMemHeroService, { delay: 0} ) // 无延迟
      HttpclientInMemoryWebApiModule.forRoot(InMemHeroService, { delay: 500 }) //延迟500ms
      
    • 默认情况下,HttpClientlnMemoryWebApiModule 模块会拦截所有的 HttpClient 请求在实际工作中,我们可能需要同时使用 HttpClient 模块和 HttpClientinMemoryWebApiModule模块,意思是同时访问外部和内存的RESTful API资源。这时,我们可以通过配置选项passThruUnknownUrl 来实现,具体代码如下。
      HttpClientInMemoryWebApiModule.forRoot(InMemHeroService,{passThruUnknownUrl: true})
      
    javascript">import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';//导入 httpClient
    import { HttpClientModule } from '@angular/common/http';
    // 导入 HttpClientInMemoryWebApiModule 注册数据存储服务
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';// 导入自己创建的 InMemHeroService 类
    import { InMemHeroService } from './in-mem-hero-service';@NgModule({declarations: [AppComponent],imports: [BrowserModule,AppRoutingModule,HttpClientModule, //导入 HttpClientModule BrowserModule 之后HttpClientInMemoryWebApiModule.forRoot(InMemHeroService) //该模块必须在 HttpClientModule 之后],providers: [],bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  4. 修改 src/app/app.components.ts 文件

    javascript">import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';@Component({selector: 'app-root',template: ``,styles: []
    })
    export class AppComponent implements OnInit {constructor(private http: HttpClient) {}ngOnInit(): void {// 获取所有的数据this.http.get('api/heroes').subscribe((data) => {console.log(data);});// 获取id为1的数据this.http.get('api/heroes/1').subscribe((data) => {console.log(data); // {id:1, name: "张三"}});// 获取name以李开头的数据this.http.get('api/heroes?name=^李').subscribe((data) => {console.log(data); // [{id:2, name: "李四"}]});}
    }
    

三、从服务器获取数据

  • HttpClient 模块允许我们在调用 HTTP 请求时使用泛型,通过泛型告诉 Angular 我们期望从HTTP 请求获得的响应类型。响应的类型可以是 any 变量类型(如 string )、类或接口等。如下面的代码执行 HttpClient 模块的 GET 请求,将预期的响应类型指定为 Hero 对象的数组。
    javascript">export class Hero {constructor(public id = 1, public name = '') {}
    }this.http.get<hero[]>(this.hreoesUrl)
    
  • 指定响应类型是给 TypeScript 看的声明,并不能保证服务器会实际使用此类型的对象进行响应。服务器 API 返回的实际响应类型是由服务器来保证的。换句话说,用户可以对Hero 类中的属性随意定义。因此,服务器实际返回的对象与类的定义并没有直接关系。
  1. 新建一个项目 demo-http

    javascript">ng new demo-http -t -s --minimal
    
  2. 安装 Angular 内存数据库

    javascript">npm i angular-in-memory-web-api@0.10.0 -S
    
  3. 新建一个 hero 接口(位置:src/app/hero.ts)

    javascript">ng g interface hero
    
  4. 修改 hero 接口文件 (位置:src/app/hero.ts)

    javascript">export interface Hero {id: number;name: string;
    }
    
  5. 新建 inMemHero 服务(位置:src/app/in-mem-hero.service.ts)

    javascript">ng g s inMemHero
    
  6. 修改 inMemHero 服务文件(位置:src/app/in-mem-hero.service.ts)

    javascript">import { Injectable } from '@angular/core';
    import { InMemoryDbService, RequestInfo } from 'angular-in-memory-web-api';
    import { Observable } from 'rxjs';@Injectable()
    export class InMemHeroService implements InMemoryDbService {createDb(reqInfo?: RequestInfo): {} | Observable<{}> | Promise<{}> {// 变量名 heroes 将被视作 URL 的一部分let heroes = [{ id: 1, name: '张三' },{ id: 2, name: '李四' },{ id: 3, name: '王五' },{ id: 4, name: '赵六' },{ id: 5, name: '孙琦' }];return { heroes };}
    }
    
  7. 编辑 src/app/app.module.ts 文件

    javascript">import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';//导入 httpClient
    import { HttpClientModule } from '@angular/common/http';
    // 导入 HttpClientInMemoryWebApiModule 注册数据存储服务
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';import { AppComponent } from './app.component';// 导入自己创建的 InMemHeroService 类
    import { InMemHeroService } from './in-mem-hero.service';@NgModule({declarations: [AppComponent],imports: [BrowserModule,HttpClientModule, //导入 HttpClientModule BrowserModule 之后HttpClientInMemoryWebApiModule.forRoot(InMemHeroService) //该模块必须在 HttpClientModule 之后],providers: [],bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  8. 编辑 src/app/app.component.ts 组件

    javascript">import { HttpClient } from '@angular/common/http';
    import { Component, OnInit } from '@angular/core';
    import { Observable } from 'rxjs';
    import { Hero } from './hero';@Component({selector: 'app-root',template: `<div style="text-align: center;"><p *ngFor="let hero of heroes">{{ hero.id }}-{{ hero.name }}</p></div>`,styles: []
    })
    export class AppComponent implements OnInit {private heroesUrl = 'api/heroes';heroes: Hero[];constructor(private http: HttpClient) {}ngOnInit(): void {this.getHeroes().subscribe((data) => {console.log(data);this.heroes = data;});}getHeroes(): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl);}
    }
    

四、HttpClient 模块的请求头配置

[1]. 添加请求头

HttpClient 方法的最后一个参数可以指定一个可选的配置对象,通过它可以对请求头进行配置。常见的配置有需要 Content-Type 标识来显式声明 HTTP 请求正文的 MIME 类型、权限认证中的Authorization 令牌以及 HTTP 请求中的参数传递等。

javascript">import { HttpClient } from '@angular/common/http';
import { HttpHeaders } from '@angular/common/http';export class DemoService {constructor(private http: HttpClient) {}getData() {const httpOptions = {headers: new HttpHeaders({'Content-Type': 'application/json'})};return this.http.get('api/heroes', httpOptions);}
}

[2]. 获取完整的响应信息

有时访问服务器,需要读取它返回的一个特殊的响应头或响应状态码,因此可能需要完整的响应信息,而不是只有响应体。在 HttpClient 模块的 get0)方法中,observe 选项可用来告诉 HttpClient 模块,希望服务器返回完整的响应信息,代码如下。

javascript">import { HttpClient } from '@angular/common/http';export class DemoService {constructor(private http: HttpClient) {}getData() {return this.http.get('api/heroes', { observe: 'response' });}
}

[3]. 配置请求参数

设置单个参数

javascript">import { HttpClient, HttpParams } from '@angular/common/http';export class DemoService {constructor(private http: HttpClient) {}searchHero(key: string) {const options = { params: new HttpParams().set('name', key) };return this.http.get('api/heroes', options);}
}

设置多个参数

javascript">new HttpParams().append('id', '1').append('name', '张三')

使用 fromString 变量通过 URL 查询字符串构建请求参数

javascript">new HttpParams({ fromString: 'id=1&name=张三'});

[4]. 请求非 JSON 数据

不是所有的 API 都会返回 JSON 数据。有时候它们会从服务器读取文本文件,并把文本文件的内容记录下来,然后把这些内容使用 Observable 的形式返回给调用者。我们可以通过在HttpClient 模块提供的 get() 方法中配置 responseType 选项来指定获取响应内容的类型。

javascript">this.http.get (filename, {responseType: 'text'})
.pipe(tap( data => console.log(filename, data))
);

五、HttpClient 模块与RxJS配合

[1]. 错误处理

  • 处理单个接口的错误:调用 HttpClient() 方法,返回可观察对象,可以在可观测对象的订阅方法中添加错误处理的逻辑代码:这种方式仅针对某个组件的接口,无法处理某一类错误

    javascript">// getHeroes(): Observable<Hero[]> {
    //   const httpOptions = {
    //     headers: new HttpHeaders({
    //       'Content-Type': 'application/json'
    //     })
    //   };//   return this.http.get<Hero[]>(this.heroesUrl);
    // }this.getHeroes().subscribe((data) => {console.log(data);this.heroes = data;},(error) => {console.log(error);}
    );
    
  • 在接口处理错误:由 HttpClient 方法返回的可观察对象通过管道传给错误处理器

    javascript">import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
    import { Observable, throwError } from 'rxjs';
    import { catchError, retry } from 'rxjs/operators';
    import { Hero } from './hero';export class DemoService {private heroesUrl = 'api/heroes';constructor(private http: HttpClient) {}getHeroes(): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl).pipe(catchError(this.handleError) // 错误处理);}private handleError(error: HttpErrorResponse) {if (error.error instanceof ErrorEvent) {// 代码运行错误或网络错误console.error('代码运行错误或网络错误:', error.error.message);} else {// 服务器发生错误,返回一个不成功的响应代码console.error(`错误码是:${error.status}, 错误信息:${error.error}`);}return throwError('系统发生错误,请稍后重试');}
    }
    

[2]. 重试

RxJS 提供了几个 retry 操作符,它们可以对失败的可观察对象自动重新订阅几次,其中最简单的是 retry0)操作符。对调用 HttpClient 方法返回的结果进行重新订阅会导致重新发起 HTTP 请求。

javascript">import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { Hero } from './hero';export class DemoService {private heroesUrl = 'api/heroes';constructor(private http: HttpClient) {}getHeroes(): Observable<Hero[]> {return this.http.get<Hero[]>(this.heroesUrl).pipe(retry(3), // 重试失败的请求,最多可重试3次catchError(this.handleError) // 错误处理);}private handleError(error: HttpErrorResponse) {if (error.error instanceof ErrorEvent) {// 代码运行错误或网络错误console.error('代码运行错误或网络错误:', error.error.message);} else {// 服务器发生错误,返回一个不成功的响应代码console.error(`错误码是:${error.status}, 错误信息:${error.error}`);}return throwError('系统发生错误,请稍后重试');}
}

六、把数据发送到服务器

[1]. 发送 POST 请求

handleError 可以看 第五章的错误处理

javascript">import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Hero } from './hero';export class DemoService {private heroesUrl = 'api/heroes';constructor(private http: HttpClient) {}addHero(hero: Hero): Observable<Hero> {const httpOptions = {headers: new HttpHeaders({'Content-Type': 'application/json'})};return this.http.post<Hero>(this.heroesUrl, hero, httpOptions);}
}

[2]. 发送 DELETE 请求

javascript">import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Hero } from './hero';export class DemoService {private heroesUrl = 'api/heroes';constructor(private http: HttpClient) {}deleteHero(hero: Hero | number): Observable<Hero> {const id = typeof hero === 'number' ? hero : hero.id;const url = this.heroesUrl + '/' + id;return this.http.delete<Hero>(url);}
}

[3]. 发送 PUT 请求

javascript">import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Hero } from './hero';export class DemoService {private heroesUrl = 'api/heroes';constructor(private http: HttpClient) {}updateHero(hero: Hero): Observable<Hero> {return this.http.put<Hero>(this.heroesUrl, hero);}
}

[4]. 用例

  1. 新建一个项目

    ng new demo-http -s -t --minimal
    
  2. 安装 Angular 内存数据库模拟服务器

    npm i angular-in-memory-web-api@0.10.0 --save
    
  3. 使用命令新建一个 hero 接口文件(位置src/app/hero.ts)

    ng g interface hero
    
  4. 修改 hero 接口文件 (位置src/app/hero.ts)

    javascript">export interface Hero {id: number;name: string;
    }
    
  5. 使用命令新建服务文件,用作请求的数据 (位置src/app/in-mem-hero.service.ts)

    javascript">ng g s inMemHero
    
  6. 修改服务文件 (位置src/app/in-mem-hero.service.ts)

    javascript">import { Injectable } from '@angular/core';
    import { InMemoryDbService, RequestInfo } from 'angular-in-memory-web-api';
    import { Observable } from 'rxjs';@Injectable()
    export class InMemHeroService implements InMemoryDbService {createDb(reqInfo?: RequestInfo): {} | Observable<{}> | Promise<{}> {// 变量名 heroes 将被视作 URL 的一部分let heroes = [{ id: 1, name: '张三' },{ id: 2, name: '李四' },{ id: 3, name: '王五' },{ id: 4, name: '赵六' },{ id: 5, name: '孙琦' }];return { heroes };}
    }
    
  7. 编辑 src/app/app.modulee.ts 模块文件

    javascript">import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';import { AppComponent } from './app.component';
    import { ReactiveFormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    import { InMemHeroService } from './in-mem-hero.service';
    import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';@NgModule({declarations: [AppComponent],imports: [BrowserModule,ReactiveFormsModule,HttpClientModule, // 须在 BrowserModule 后面HttpClientInMemoryWebApiModule.forRoot(InMemHeroService) // 须在 HttpClientModule 后面],providers: [],bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  8. 使用命令新建服务,用于发送 http 请求(位置:src/app/hero.service.ts)

    javascript">ng g s hero
    
  9. 修改src/app/hero.service.ts 文件

    javascript">import { HttpClient, HttpHeaders } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable, of } from 'rxjs';
    import { tap, catchError } from 'rxjs/operators';
    import { Hero } from './hero';@Injectable({providedIn: 'root'
    })
    export class HeroService {// 内存数据库的 REST API 地址private herosUrl = 'api/heroes';// 请求头httpOptions = {headers: new HttpHeaders({'Content-Type': 'application/json'})};constructor(private http: HttpClient) {}// 用作处理请求产生的错误private handleError<T>(operation = 'operation', result?: T) {return (error: any): Observable<T> => {console.log(`${operation} 失败:${error.message}`);return of(result as T); // 返回可观察对象};}getHeroes(): Observable<Hero[]> {return this.http.get<Hero[]>(this.herosUrl).pipe(tap((_) => console.log('获取所有数据')),catchError(this.handleError<any>('getHeroes')));}addHero(hero: Hero): Observable<Hero> {return this.http.post<Hero>(this.herosUrl, hero, this.httpOptions).pipe(tap((newHero: Hero) => console.log(`添加的 hero 的id=${newHero.id}`)),catchError(this.handleError<Hero>('addHero')));}deleteHero(hero: Hero | number): Observable<Hero> {const id = typeof hero === 'number' ? hero : hero.id;const url = `${this.herosUrl}/${id}`;return this.http.delete<Hero>(url, this.httpOptions).pipe(tap((_) => console.log(`删除的 hero 的id=${id}`)),catchError(this.handleError<any>('deleteHero', hero)));}updateHero(hero: Hero): Observable<Hero> {hero.name = hero.name + (hero.id + 1);return this.http.put<Hero>(this.herosUrl, hero, this.httpOptions).pipe(tap((_) => console.log(`更新 hero id=${hero.id}`), catchError(this.handleError('updateHero', hero))));}
    }
    
  10. 编辑 src/app/app.component.ts 文件

    javascript">import { Component, OnInit } from '@angular/core';
    import { Hero } from './hero';
    import { FormBuilder, FormGroup, Validators } from '@angular/forms';
    import { HeroService } from './hero.service';@Component({selector: 'app-root',template: `<div><table><tr><th>ID</th><th>姓名</th><th>操作</th></tr><tr *ngFor="let hero of heroes"><td>{{ hero.id }}</td><td>{{ hero.name }}</td><td><button (click)="deleteHero(hero.id)">删除</button><button (click)="updateHero(hero)">更新</button></td></tr></table><br /><form [formGroup]="formGroup" (ngSubmit)="onSubmit()"><div class="block"><label>Id:</label><input type="text" formControlName="id" /></div><div class="block"><label>姓名:</label><input type="text" formControlName="name" /></div><input type="submit" value="添加" [disabled]="!formGroup.valid" /><br /><br />表单是否有效:{{ formGroup.valid }}<br />表单完整数据:{{ formGroup.valid | json }}<br /></form></div>`,styles: ['form { border: 1px solid red; } ', '.block label { display: inline-block; width: 50px; text-align: right; }']
    })
    export class AppComponent implements OnInit {heroes: Hero[];formGroup: FormGroup;constructor(private heroService: HeroService, private fb: FormBuilder) {}ngOnInit(): void {this.getHeroes();// 初始化表单this.formGroup = this.fb.group({id: this.fb.control('', Validators.required),name: this.fb.control('', Validators.required)});}getHeroes() {this.heroService.getHeroes().subscribe((data) => (this.heroes = data));}updateHero(hero: Hero) {this.heroService.updateHero(hero).subscribe((data) => {console.log('修改数据:', data);this.getHeroes();});}deleteHero(id: number) {this.heroService.deleteHero(id).subscribe((data) => {console.log('删除数据', data);this.getHeroes();});}onSubmit() {const hero = this.formGroup.value;hero.id = Number(hero.id);this.heroService.addHero(hero).subscribe((hero) => {if (hero) {this.getHeroes();} else {alert('发送错误');}this.formGroup.reset();});}
    }
  11. 页面如下

七、Angular 拦截器

Angular 中的拦截器(Httplnterceptor 接口)提供了一种拦截 HTTP 请求和 HTTP 响应的方法,可以用来监视与转换 HTTP 请求和 HTTP 响应。 拦截器使用一种常规的、标准的方法对每一次 HTTP 的请求和响应任务执行如认证、添加请求参数和记录日志等很多种隐式任务。 如果没有拦截器,那么开发者将不得不对 HttpClient 模块的每次调用显式地执行这些任务。

[1]. 创建拦截器

  • 要创建拦截器,就要创建一个实现了 Httplnterceptor 接口的类,并实现该接口中的 intercept()方法。用户可以使用如下的 Anqular CLI命令创建拦截器。
    ng generate interceptor <name>
    
  1. 新建一个拦截器 my,将生成 src/app/my.interceptor.ts 文件

    javascript">ng g interceptor my
    
  2. src/app/my.interceptor.ts 文件内容如下

    javascript">import { Injectable } from '@angular/core';
    import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
    import { Observable } from 'rxjs';@Injectable()
    export class MyInterceptor implements HttpInterceptor {constructor() {}/**** request: 请求对象实例* next: 拦截器链表中的下一个拦截器*/intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {// 输入请求信息,只有这一行是添加的,别的都是生成的console.log(JSON.stringify(request));// 继续向下走return next.handle(request);}
    }
    

[2]. 配置拦截器提供商

  • 在Angular 中配置提供商后,应用程序就可以使用提供商来配置注入器了。注入器负责提供依赖注入服务,进而 Web 应用程序就能使用依赖注入服务了。因此,在创建了拦截器后,我们还需要进一步配置拦截器提供商。

  • 由于拦截器是 HttpClient 服务的(可选)依赖,因此必须在提供 HttpClient 服务的同一个(或其各级父注入器)注入器中提供这些拦截器。我们在根模块 AppModule 中导入了HttoClientModule 模块,导致 Web 应用程序在其根注入器中提供了 HtpClient 服务,所以也要在根模块 AppModule 中提供这些拦截器。配置拦截器提供商的注册语句格式如下。

    javascript">@NgModule({providers: [{ provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }],
    })
    
  • 在上述代码中,我们在@NgModule() 装饰器的元数据的 providers 选项里配置拦截器提供商。其中 provide 选项值HTTP_INTERCEPTORS 常量来自 @angular/common/http 包; useClass选项值是我们创建的拦截器;multi 选项值为 true,表示当前注入的是一个数组的值,而不是单一的值,multi 选项值默认为 true。如果在 Web 应用程序中仅配置一个拦截器提供商,那么程序代码也可以直接写成如下形式。

    javascript">@NgModule({providers: [MyInterceptor],
    })
    

[3]. 用例

配置日志和错误信息的拦截器

  1. 新建一个项目

    ng new demo-http4 -s -t --minimal --routing=false --style=css
    
  2. 新建一个日志拦截器文件(位置:src/app/log.interceptor.ts)

    ng g interceptor log
    
  3. 修改日志拦截器文件(位置:src/app/log.interceptor.ts)

    javascript">import { Injectable } from '@angular/core';
    import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpResponse } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { tap, finalize } from 'rxjs/operators';@Injectable()
    export class LogInterceptor implements HttpInterceptor {constructor() {}intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {const started = Date.now();let ok: string;return next.handle(request).pipe(tap(// 正常是返回 HttpResponse 类型对象(event) => {console.log('进入Log 拦截器');ok = event instanceof HttpResponse ? 'succeeded' : '';},// 错误时返回 HttpErrorResponse 类型对象(error) => (ok = 'failed')),// 当 HTTP 请求调用完成或者有错误发生时执行下面的逻辑finalize(() => {const elapsed = Date.now() - started;const msg = `${request.method} "${request.urlWithParams}" ${ok} in ${elapsed} ms.`;console.log('Log拦截器' + msg); //输入请求信息}));}
    }
    
  4. 新建一个错误拦截器文件(位置:src/app/error.interceptor.ts)

    ng g interceptor error
    
  5. 修改错误拦截器文件(位置:src/app/error.interceptor.ts)

javascript">import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';@Injectable()
export class ErrorInterceptor implements HttpInterceptor {constructor() {}intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {return next.handle(request).pipe(tap((data) => console.log('进入 error 拦截器,没有发生错误', data),catchError((err) => {if (err.status === 401) {console.log('进入 error 拦截器,发生了 401 错误');}const error = err.error.message || err.statusText;return throwError(error);})));}
}
  1. 编辑 src/app/app.module.ts 文件

    javascript">import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';import { HTTP_INTERCEPTORS } from '@angular/common/http';
    import { HttpClientModule } from '@angular/common/http';import { AppComponent } from './app.component';
    import { LogInterceptor } from './log.interceptor';
    import { ErrorInterceptor } from './error.interceptor';@NgModule({declarations: [AppComponent],imports: [BrowserModule, HttpClientModule],providers: [ // angular 会按照顺序依次进行拦截{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },{ provide: HTTP_INTERCEPTORS, useClass: LogInterceptor, multi: true }],bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  2. 新建 user 接口文件(位置:src/app/user.ts)

    ng g interface user
    
  3. 修改 user 接口文件(位置:src/app/user.ts)

    javascript">export interface User {login: string;url: string;
    }
    
  4. 新建一个 github 服务类文件(位置:src/app/github.service.ts)

    javascript">ng g s github
    
  5. 修改 github 服务类文件(位置:src/app/github.service.ts)

    javascript">import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { User } from './user';@Injectable({providedIn: 'root'
    })
    export class GithubService {// github 的接口private usersUrl = 'https://api.github.com/users?since=1';constructor(private http: HttpClient) {}getUsers(): Observable<User[]> {return this.http.get<User[]>(this.usersUrl);}
    }
    
  6. 修改 src/app/app.component.ts 文件

    javascript">import { Component, OnInit } from '@angular/core';
    import { Observable } from 'rxjs';
    import { GithubService } from './github.service';
    import { User } from './user';@Component({selector: 'app-root',template: ` <div><h3>从github 上获取 users</h3><div *ngFor="let user of users$ | async"><strong>User Name: </strong>{{ user.login }} <strong>GitHub URL: </strong>{{ user.url }}</div></div>`,styles: []
    })
    export class AppComponent implements OnInit {users$: Observable<Array<User>>;constructor(private githubService: GithubService) {}ngOnInit(): void {this.users$ = this.githubService.getUsers();}
    }
    
  7. 页面展示,控制台将输出拦截的信息,两个拦截器分别进入了两次,请求时一次,响应时一次


http://www.ppmy.cn/devtools/149777.html

相关文章

Python Matplotlib 教程-Matplotlib 如何绘制常见图表

Python Matplotlib 如何绘制常见图表 Matplotlib 是 Python 中最流行的数据可视化库之一&#xff0c;提供了多种方式绘制各种图表&#xff0c;如折线图、柱状图、散点图、饼图等。本篇文章将从基础入门开始&#xff0c;逐步介绍如何使用 Matplotlib 绘制这些常见图表&#xff…

T-SQL语言的网络编程

T-SQL语言的网络编程探索 引言 随着互联网的快速发展&#xff0c;数据的存储与管理变得愈发重要。尤其是在面向服务的架构&#xff08;SOA&#xff09;以及微服务架构中&#xff0c;数据库的作用愈加凸显。在这种背景下&#xff0c;T-SQL&#xff08;Transact-SQL&#xff09…

T-SQL语言的编程范式

T-SQL编程范式探析 引言 随着信息技术的迅猛发展&#xff0c;数据库在各个行业的应用日益广泛。在众多数据库管理系统中&#xff0c;SQL Server以其高性能和易用性受到广泛欢迎。T-SQL&#xff08;Transact-SQL&#xff09;是SQL Server的扩展版本&#xff0c;是一种用于查询…

基于Spring Boot的健康饮食管理系统

一、系统架构与技术栈 系统架构&#xff1a;系统通常采用典型的三层架构设计&#xff0c;分为表现层、业务逻辑层和数据访问层。表现层负责与用户进行交互&#xff0c;展示信息和接收用户输入&#xff1b;业务逻辑层处理系统的核心业务&#xff0c;如用户信息管理、饮食记录分…

Flutter路由动画Hero函数的使用

Hero用于实现两个页面切换的过渡效果 以下示例&#xff1a;展示的是HeroPage切换到Hero2Page过程中 图片平滑放大的过程 Hero函数有两个重要的参数 tag&#xff1a;标识两个路由切换的共享参数&#xff0c;因为是HeroPage到Hero2Page切换&#xff0c;这里指定tag是tag1 chl…

【大模型 RAG技术】Elasticsearch (ES) 构建一个基于 RAG问答系统

要利用 Elasticsearch (ES) 构建一个基于 RAG&#xff08;Retrieval-Augmented Generation&#xff09;的应用&#xff0c;你可以按照以下步骤进行&#xff1a; 1. 准备数据 首先&#xff0c;你需要将 result.txt 文件中的数据转换为适合 Elasticsearch 的格式。假设你的数据…

c++入门之 命名空间与输入输出

1、命名空间 1.1使用命名空间的原因 先看一个例子&#xff1a; #include <iostream>int round 0;int main() {printf("%d", round);return 0; }请问&#xff0c;这个程序能跑起来吗&#xff1f; 答案是否定的 原因是&#xff0c;当我们想创建一个全局变量 …

Linux下常用的IO模型

Linux下常用的IO模型&#xff1a; 1. 阻塞IO fgets read getchar fread fgetc recv recvfrom 1. 让多个IO事件以同步方式处理&#xff0c; 2. 多个IO事件之间互相影响 3. CPU占有率低 2. 非阻塞IO 将IO对应的文件描述符设置成…