angular HttpClient

參考:angular httpClient文檔

要想使用 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還支持修改型的請求,通過 PUTPOSTDELETE 這樣的 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 安全防護

配置自定義 cookie/header 名稱
如果你的後端服務中對 XSRF 令牌的 cookie 或 頭使用了不一樣的名字,就要使用 HttpClientXsrfModule.withConfig() 來覆蓋掉默認值。

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 })

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章