Angular HttpClient 封裝

HttpClient 是 Angular 對 XMLHttpRequest 和 Fetch 的封裝
理解 HttpClient

provideHttpClient 源碼在 provider.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
export function provideHttpClient(
...features: HttpFeature<HttpFeatureKind>[]
): EnvironmentProviders {
if (ngDevMode) {
const featureKinds = new Set(features.map((f) => f.ɵkind));
if (
featureKinds.has(HttpFeatureKind.NoXsrfProtection) &&
featureKinds.has(HttpFeatureKind.CustomXsrfConfiguration)
) {
throw new Error(
ngDevMode
? `Configuration error: found both withXsrfConfiguration() and withNoXsrfProtection() in the same call to provideHttpClient(), which is a contradiction.`
: '',
);
}
}

const providers: Provider[] = [
// 1. HttpClient 是最常用的Service Providers
HttpClient,
HttpXhrBackend,
HttpInterceptorHandler,
{provide: HttpHandler, useExisting: HttpInterceptorHandler},
// 2. default Angular 內部使用XMLHttpRequest發送請求
{provide: HttpBackend, useExisting: HttpXhrBackend},
{
provide: HTTP_INTERCEPTOR_FNS,
useValue: xsrfInterceptorFn,
multi: true,
},
{provide: XSRF_ENABLED, useValue: true},
{provide: HttpXsrfTokenExtractor, useClass: HttpXsrfCookieExtractor},
];

for (const feature of features) {
providers.push(...feature.ɵproviders);
}
// 製作環境提供者
return makeEnvironmentProviders(providers);
}
  • HttpClient 是最常用的Service Providers
  • Angular 默認內部使用XMLHttpRequest發送請求

全局載入

  1. 添加 HttpClient 相關的 providers
    app.config.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
    import { provideRouter } from '@angular/router';
    + import {provideHttpClient} from '@angular/common/http';
    import { routes } from './app.routes';
    import { provideClientHydration, withEventReplay } from '@angular/platform-browser';

    export const appConfig: ApplicationConfig = {
    providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    // 添加 HttpClient 相關的 providers
    + provideHttpClient(),
    provideRouter(routes),
    provideClientHydration(withEventReplay())
    ]
    };
    Fetch 不支持上傳進度,這個是 Fetch 目前最大的缺陷,這也是為什么 Angular 任然以 XMLHttpRequest 作为默認。

src/app 新增 api 資料夾 => 新增 web-api.service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class WebApiService {
private readonly http = inject(HttpClient);

private readonly httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

get(url: string): Observable<any> {
return this.http.get(url, this.httpOptions)
.pipe(catchError(this.handleError));
}

post(url: string, model: any): Observable<any> {
return this.http.post(url, model, this.httpOptions)
.pipe(catchError(this.handleError));
}

put(url: string, model: any): Observable<any> {
return this.http.put(url, model, this.httpOptions)
.pipe(catchError(this.handleError));
}

delete(url: string): Observable<any> {
return this.http.delete(url, this.httpOptions)
.pipe(catchError(this.handleError));
}

private handleError(error: any) {
console.error('API 錯誤:', error);
return throwError(() => new Error(error.message || '錯誤已消失'));
}
}

src/app 新增 api 資料夾=> 新增http-provider.service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { WebApiService } from './web-api.service';
import { environment } from '../../environments/environment';
import dayjs from 'dayjs'
@Injectable({ providedIn: 'root' })
export class HttpProviderService {
private readonly apiUrl = environment.apiUrl;
private readonly webApiService = inject(WebApiService);

getAllLists(): Observable<any> {
return this.webApiService.get(`${this.apiUrl}/api/toDoLists`);
}
// 編輯
deleteTodoById(_id: string): Observable<any> {
return this.webApiService.delete(`${this.apiUrl}/api/toDo/${encodeURIComponent(_id)}`);
}

getTodoById(id: string): Observable<any> {
return this.webApiService.get(`${this.apiUrl}/api/toDo/${encodeURIComponent(id)}`);
}
// 新增
addList(newTask: string): Observable<any> {
let query:any = {
title: newTask,
buildDate: Date.now(),
updataDate: Date.now(),
}
return this.webApiService.post(`${this.apiUrl}/api/toDo`,query);
}

// 編輯
updateTodo(item: any): Observable<any> {
console.log('updateTodo', item)
let query:any = {
title: item.title,
buildDate: dayjs(item.buildDate).valueOf(),//改為時間搓
updataDate: Date.now(),
}
return this.webApiService.put(`${this.apiUrl}/api/toDo/${encodeURIComponent(item._id)}`, query);
}
}

參考資料