要想使用 HttpClient
,就要先導入 Angular 的 HttpClientModule
。大多數應用都會在根模塊 AppModule
中導入它!!!!。
1.簡單的獲取 JSON 數據
// assets/config.json
{
"heroesUrl": "api/heroes",
"textfile": "assets/textfile.txt"
}
// app/config/config.service.ts
configUrl = 'assets/config.json';
getConfig() {
return this.http.get(this.configUrl);
}
// app/config/config.component.ts 使用
showConfig() {
this.configService.getConfig()
.subscribe((data: Config) => this.config = {
heroesUrl: data['heroesUrl'],
textfile: data['textfile']
});
}
2.獲取錯誤詳情,
請求導致了服務器錯誤怎麼辦?
通過在 .subscribe()
中添加第二個回調函數,可以在組件中處理它:
app/config/config.component.ts
showConfig() {
this.configService.getConfig()
.subscribe(
(data: Config) => this.config = { ...data }, // success path
error => this.error = error // error path
);
}
封裝下:首先要設計一個錯誤處理器,就像這樣
app/config/config.service.ts (handleError),全局錯誤方法
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
return throwError('Something bad happened; please try again later.');
};
現在,獲取了由 HttpClient
方法返回的 Observable
,並把它們通過管道傳給錯誤處理器。
app/config/config.service.ts
getConfig() {
return this.http.get<Config>(this.configUrl)
.pipe(
retry(3), // 重新請求3次
catchError(this.handleError)
);
}
3.把數據發送到服務器
除了從服務器獲取數據之外(get), HttpClient還支持修改型的請求,通過 PUT
、POST
、DELETE
這樣的 HTTP 方法把數據發送到服務器。
發起一個 POST 請求,(要添加請求頭)
app/heroes/heroes.service.ts
import { HttpHeaders } from '@angular/common/http';
// 設置公共請求頭
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
};
// 添加一條hero數據
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
// 在組組件中使用:this.heroesService.addHero(newHero).subscribe(hero => this.heroes.push(hero));
發起 DELETE
請求
app/heroes/heroes.service.ts添加代碼:
/** 刪除id爲..的hero */
deleteHero (id: number): Observable<{}> {
const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42
return this.http.delete(url, httpOptions)
.pipe(
catchError(this.handleError('deleteHero'))
);
}
// 組件中使用:this.heroesService.deleteHero(hero.id).subscribe();
// 切記:必須調用 subscribe()訂閱。僅僅調用 HeroesService.deleteHero() 是不會發起 DELETE 請求的
發起 PUT 請求
app/heroes/heroes.service.ts添加代碼:
/** 更新服務器上的英雄。成功後返回更新的英雄. */
updateHero (hero: Hero): Observable<Hero> {
return this.http.put<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('updateHero', hero))
);
}
高級用法
1.set()方法
修改請求頭
直接修改前述配置對象中的現有頭,因爲這個 HttpHeaders
類的實例是不可變的,
改用 set()
方法代替。 它會返回當前實例的一份克隆,其中應用了這些新修改。
比如在發起下一個請求之前,如果舊的令牌已經過期了,你可能還要修改認證頭。
httpOptions.headers = httpOptions.headers.set('Authorization', 'my-new-auth-token');
2.set()方法
設置URL 參數
添加 URL 搜索參數也與此類似。 這裏的 searchHeroes
方法會查詢名字中包含搜索詞的英雄列表。
/* 獲得名字包含搜索詞的英雄*/
searchHeroes(term: string): Observable<Hero[]> {
term = term.trim();
// 如果有搜索項,則添加安全的、url編碼的搜索參數,HttpParams 是不可變的,所以要使用 set() 方法來修改這些選項
const options = term ? { params: new HttpParams().set('name', term) } : {};
return this.http.get<Hero[]>(this.heroesUrl, options)
.pipe(
catchError(this.handleError<Hero[]>('searchHeroes', []))
);
}
3.請求的防抖(debounce) 請移步官網文檔的小案例:angular 請求防抖
4.switchMap()
這個 switchMap()
操作符有三個重要的特徵:
①.它的參數是一個返回 Observable
的函數。PackageSearchService.search
會返回 Observable
,其它數據服務也一樣。
②.如果以前的搜索結果仍然是在途狀態(這會出現在慢速網絡中),它會取消那個請求,併發起一次新的搜索。
③.它會按照原始的請求順序返回這些服務的響應,而不用關心服務器實際上是以亂序返回的它們。
5.攔截請求和響應
HTTP 攔截機制是 @angular/common/http
中的主要特性之一。 使用這種攔截機制,你可以聲明一些攔截器,用它們監視和轉換從應用發送到服務器的 HTTP 請求。 攔截器還可以用監視和轉換從服務器返回到本應用的那些響應。 多個選擇器會構成一個“請求/響應處理器”的雙向鏈表。
如果沒有攔截機制,那麼開發人員將不得不對每次 HttpClient
調用顯式實現這些任務
編寫攔截器
要實現攔截器,就要實現一個實現了 HttpInterceptor
接口中的 intercept()
方法的類。這裏是一個什麼也不做的空白攔截器,它只會不做任何修改的傳遞這個請求。
app/http-interceptors/noop-interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { Observable } from 'rxjs';
/** 將未觸及的請求傳遞給下一個請求處理程序。 */
@Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req);
}
}
app/http-interceptors/index.ts
/* http攔截器的“桶” */
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NoopInterceptor } from './noop-interceptor';
/** 外部的http攔截器提供程序 */
export const httpInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },
];
app/app.module.ts
providers: [
httpInterceptorProviders
],
攔截器的順序
Angular 會按照你提供它們的順序應用這些攔截器。 如果你提供攔截器的順序是先 A,再 B,再 C,那麼請求階段的執行順序就是 A->B->C,而響應階段的執行順序則是 C->B->A。
以後你就再也不能修改這些順序或移除某些攔截器了。 如果你需要動態啓用或禁用某個攔截器,那就要在那個攔截器中自行實現這個功能。
不可變性
雖然攔截器有能力改變請求和響應,但 HttpRequest
和 HttpResponse
實例的屬性卻是隻讀(readonly
)的, 因此讓它們基本上是不可變的
通過把 HttpRequest
的屬性設置爲只讀的,TypeScript 可以防止你犯這種錯誤。
req.url = req.url.replace('http://', 'https://');
要想修改該請求,就要先克隆它,並修改這個克隆體,然後再把這個克隆體傳給 next.handle()
。 你可以用一步操作中完成對請求的克隆和修改,例子如下:
app/http-interceptors/ensure-https-interceptor.ts
// 同時克隆請求並將“http://”替換爲“https://”
const secureReq = req.clone({
url: req.url.replace('http://', 'https://')
});
// 將克隆的“安全”請求發送到下一個處理程序。
return next.handle(secureReq);
這個 clone()
方法的哈希型參數允許你在複製出克隆體的同時改變該請求的某些特定屬性。
如果你必須修改請求體,那麼就要先複製它,然後修改這個副本,clone()
這個請求,然後把這個請求體的副本作爲新的請求體,例子如下:
app/http-interceptors/trim-name-interceptor.ts
// 複製正文並從name屬性中修剪空白
const newBody = { ...body, name: body.name.trim() };
// 克隆請求並設置其主體
const newReq = req.clone({ body: newBody });
// 將克隆的請求發送到下一個處理程序。
return next.handle(newReq);
清空請求體
newReq = req.clone({ ... }); // 未提及正文=>保留原始正文
newReq = req.clone({ body: undefined }); // 保留原始實體
newReq = req.clone({ body: null }); // 清空請求體
設置默認請求頭
app/http-interceptors/auth-interceptor.ts
import { AuthService } from '../auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
// 從服務獲取身份驗證令牌
const authToken = this.auth.getAuthorizationToken();
// 克隆請求並將原始頭替換爲
// 克隆的頭,使用授權更新。
const authReq = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// const authReq = req.clone({ setHeaders: { Authorization: authToken } });
// 將帶有頭的克隆請求發送到下一個處理程序。
return next.handle(authReq);
}
}
攔截器還可以做些什麼?
1.記日誌 參考:angular官網-攔截器記日誌
2.緩存 參考:angular官網-攔截器緩存
安全:XSRF 防護 參考:angular 安全防護
imports: [
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'My-Xsrf-Cookie',
headerName: 'My-Xsrf-Header',
}),
],
總結:
post請求(新增數據):this.http.post<Hero>(url, params, httpHeaderOption) // httpHeaderOption設置的請求頭
delete請求(刪除數據):this.http.delete(url, httpHeaderOption)
put請求(修改數據):this.http.put<Hero>(url, params, httpHeaderOption)
get請求(獲取數據):this.http.get(url);
攔截器:intercept(req, next){... return next(setreq)}, // setreq是對req進行修改過後的請求。
set()方法
設置URL 參數set()方法
修改請求頭
httpOptions.headers = httpOptions.headers.set('Authorization', 'my-new-auth-token');//還可以使用setHeaders({key:value})
params = { params: new HttpParams().set('name', term) }
get(): resp.headers.get(key)
HttpRequest
和 HttpResponse
實例的屬性都是隻讀的,不可修改,
例如:req.url = req.url.replace('http://', 'https://');,是錯誤的,只能clone一份出來,再進行修改
清空請求體:req.clone({ body: null })