Angular8 HttpClient 30分钟深入了解下

2,167 阅读2分钟

Angular8 HttpClient 30分钟深入了解下

前端开发,axios是标配的http请求发起libary, 采用的是Promise的方式。然后,Angular中采用的是另外一种形式Observable,观察订阅模式。Angular默认推荐采用内置的HTTPClient。 下面让我们开始今天的主题,HTTPClient

模块引入

import {HttpClientModule} from '@angular/common/http';
@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        HttpClientModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {
}

入门

GET请求

指定请求返回类型
this.http.get<Config>(this.configUrl)
  .subscribe((data: Config) => this.config = { ...data });
  • get请求,明确返回的数据类型为Config,故请求形式为:
this.http.get<Config>()...
  • 请求返回后,进行数据转换
不指定请求返回类型
this.http.get(this.configUrl)
.subscribe((data: any) => this.config = { ...data });

等效于

this.http.get<Object>(this.configUrl)
.subscribe((data: Object) => this.config = { ...data });

问题1: 如果服务端,返回的数据就是一个text文本,譬如Hello,world,你猜会怎么样?

请求url携带参数
  • 方法一:HttpParams 形式set
# 必须.链式set,否则参数空
const params = new HttpParams()
    .set('orderBy', '"$key"')
    .set('limitToFirst', "1");
this.http.get(this.configUrl,{params})
  .subscribe((data: any) => this.config = { ...data });
  • 方法二: fromString
const params = new HttpParams({
  fromString: 'orderBy="$key"&limitToFirst=1'
});

this.http.get(this.configUrl,{params}) .subscribe((data: any) => this.config = { ...data });

问题2: 如果前端想拿到后端api header头中参数,怎么办?

POST

this.http.post(url,
    {
        "courseListIcon": "...",
        "description": "TEST",
        "iconUrl": "..",
        "longDescription": "...",
        "url": "new-url"
    })
    .subscribe(
        (res) => {
            console.log("POST call successful value returned in body", 
                        res);
        },
        error => {
            console.log("POST call in error", error);
        },
        () => {
            console.log("The POST observable is now completed.");
        });
}

DELETE

this.http.delete(url1)
    .subscribe(
        (res) => {
            console.log("DELETE call successful value returned in body", 
                        res);
        },
        error => {
            console.log("DELETE call in error", error);
        },
        () => {
            console.log("The DELETE observable is now completed.");
        });
}

PATCH

this.http.patch(url,
    {
        "description": "Angular Tutorial For Beginners PATCH TEST",
    })
    .subscribe(
        (res) => {
            console.log("PATCH call successful value returned in body", 
                        res);
        },
        error => {
            console.log("PATCH call in error", error);
        },
        () => {
            console.log("The PATCH observable is now completed.");
        });
}

进阶

GET请求

request方式传参
const params = new HttpParams({
 fromString: 'orderBy="$key"&limitToFirst=1'
});
this.http.request("GET",this.configUrl, { params })
header传参
  • 方法一: HttpHeaders
const headers = new HttpHeaders()
         .set("X-CustomHeader", "custom header value");
this.http.get(this.configUrl,{ headers })
 .do(console.log)
 .map(data => _.values(data));
  • 方法二:{} 字面量
const headers = {
  "X-CustomHeader", "custom header value",
  'content-type': 'application/json'
}
this.http.get(this.configUrl,{ headers })
 .do(console.log)
 .map(data => _.values(data));
解答问题1:
  • 我们看一下源码针对Get方法请求参数的定义
get(url: string, options?: {
      headers?: HttpHeaders | {
          [header: string]: string | string[];
      };
      # 默认值有: response| body| event 
      observe?: 'body';# 默认读取的是response中的body 
      params?: HttpParams | {
          [param: string]: string | string[];
      };
      reportProgress?: boolean;
      # 默认值有: arraybuffer | json | blob |text
      responseType?: 'json';# 这里,ts参数类型可选,默认值json,
      withCredentials?: boolean;
  }): Observable<Object>;
  • 故从源码我们可以知道,后端返回Hello,world,前端get方法会返回JSON解析异常。此时我们设置下responseType即可。
this.http.get(this.configUrl,{responseType:'text'})
.subscribe((data: any) => this.config = { ...data });
解答问题二:
  this.http.get<Config>(
     this.configUrl, { observe: 'response' })
    .subscribe((data: any) => this.config = { ...data });
那么event 是干什么的呢?
  const request = new HttpRequest(
        "POST", this.uploadURL, {},{observe: 'events',reportProgress: true});
    this.http.request(request)
        .subscribe(
            event => {
                # 文件上传进度判定
                if (event.type === HttpEventType.DownloadProgress) {
                    console.log("Download progress event", event);
                }
                if (event.type === HttpEventType.UploadProgress) {
                    console.log("Upload progress event", event);
                }
                if (event.type === HttpEventType.Response) {
                    console.log("response received...", event.body);
                }
            }
        );

高级

GET请求

并行多个Get处理
const parallel$ = Observable.forkJoin(
        this.http.get(url1),
        this.http.get(url2)
    );
parallel$.subscribe(
    values => {
        console.log("all values", values)
    }
);
串行多个Get请求
const sequence$ = this.http.get<Config>(url1)
    .switchMap(config => {
        config.description+= ' - TEST ';
        return this.http.put(url2,config)
    });
sequence$.subscribe(
    values => console.log("result observable ", values) 
);
异常处理
this.http
    .get("/api/simulate-error")
    .catch( error => {
        // here we can show an error message to the user,
        // for example via a service
        console.error("error catched", error);
        return Observable.of({description: "Error Value Emitted"});
    })
    .subscribe(
        val => console.log('Value emitted successfully', val),
        error => {
            console.error("This line is never called ",error);
        },
        () => console.log("HTTP Observable completed...")
    );
请求拦截
  • 定义鉴权拦截器

import {Injectable} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor} 
     from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor(private authService: AuthService) {
    }
    intercept(req: HttpRequest<any>, 
               next: HttpHandler):Observable<HttpEvent<any>> {
        const clonedRequest = req.clone({
            headers: req.headers.set(
                'X-CustomAuthHeader', 
                authService.getToken())
        });
        console.log("new headers", clonedRequest.headers.keys());
        return next.handle(clonedRequest);
    }
}
  • 配置拦截器
@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        HttpClientModule
    ],
    providers: [
        [ 
           { 
            provide: HTTP_INTERCEPTORS, 
            useClass: AuthInterceptor,
            multi: true 
          }
        ]
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}

参考

推荐

Angular8状态管理NgRx

前端Rollup+RxJs响应式编程实践

Angular8 httpclient简单入门

20个你值得了解的Angular开源项目

angular8 日常开发填坑指南

Angular开发必备插件一览表

Jasmine编写测试用例so easy ~

Angular8单元测试示例指南

Rxjs操作符实战指南