大多数前端应用都要通过 HTTP 协议与服务器通讯,才能下载或上传数据并访问其它后端服务。Angular 给应用提供了一个 HTTP 客户端 API,也就是 @angular/common/http 中的 HttpClient 服务类。
1、HttpClient的使用
要想使用 HttpClient,就要先导入 Angular 的 HttpClientModule。一般都是在根模块 AppModule 中进行导入。
app/app.module.ts:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
// ... other modules ...
],
declarations: [
AppComponent,
],
bootstrap: [ AppComponent ]
})
export class AppModule {}
然后把 HttpClient 服务注入成一个服务类的依赖项,在服务类的方法中进行使用:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class ApiService {
constructor(private http: HttpClient) { }
getUserList(){
const url = "/api/user/list";
return this.http.get<any>(url);
}
}
然后在组件中调用服务获取数据进行处理:
import { Component } from '@angular/core';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent {
userList:Array<any> = [];
constructor(private api: ApiService) { }
getUserList(){
this.api. getUserList.subscribe(data=>{
this.userList = data;
});
}
}
2、常用请求方法
HttpClient 类包含了 get post delete put 等常见的 HTTP 请求方法。主要的方法声明如下:
get<T>(url: string, options?: {
headers?: HttpHeaders | { [header: string]: string | string[]; };
observe?: 'body';
params?: HttpParams | { [param: string]: string | string[]; };
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}): Observable<T>;
post<T>(url: string, body: any | null, options: {
headers?: HttpHeaders | { [header: string]: string | string[]; };
observe: 'response';
params?: HttpParams | { [param: string]: string | string[]; };
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}): Observable<HttpResponse<T>>;
delete<T>(url: string, options?: {
headers?: HttpHeaders | { [header: string]: string | string[]; };
observe?: 'body';
params?: HttpParams | { [param: string]: string | string[]; };
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}): Observable<T>;
put<T>(url: string, body: any | null, options?: {
headers?: HttpHeaders | { [header: string]: string | string[]; };
observe?: 'body';
params?: HttpParams | { [param: string]: string | string[]; };
reportProgress?: boolean;
responseType?: 'json';
withCredentials?: boolean;
}): Observable<T>;
这里只主要列举了四个常用的方法,实际上还有 request, head, options, patch, jsonp 等方法,并且相同的方法名也存在多个重载方法。项目中可以根据实际需求调用合适的方法。
可以看到所有请求的方法返回值都是 Observable 可观察对象。
3、调用关系
查阅 HttpClient 类的源代码,可以发现全部的请求方法都是通过传递不同参数来调用 request方法:
get(url, options = {}) {
return this.request('GET', url, (/** @type {?} */ (options)));
}
post(url, body, options = {}) {
return this.request('POST', url, addBody(options, body));
}
delete(url, options = {}) {
return this.request('DELETE', url, (/** @type {?} */ (options)));
}
put(url, body, options = {}) {
return this.request('PUT', url, addBody(options, body));
}
request 方法关键代码如下:
// Start with an Observable.of() the initial request, and run the handler (which
// includes all interceptors) inside a concatMap(). This way, the handler runs
// inside an Observable chain, which causes interceptors to be re-run on every
// subscription (this also makes retries re-run the handler, including interceptors).
/** @type {?} */
const events$ = of (req).pipe(concatMap((req) => this.handler.handle(req)));
......
// The requested stream contains either the full response or the body. In either
// case, the first step is to filter the event stream to extract a stream of
// responses(s).
/** @type {?} */
const res$ = ( /** @type {?} */ (events$.pipe(filter((event) => event instanceof HttpResponse))));
......
可以看到主要的步骤包括:
1、of (req) : 使用 RxJS 的 of 操作符基于请求信息新建的一个单播的 Cold Observable (数据流,原始的数据就是请求信息) 。
2、pipe(concatMap((req) => this.handler.handle(req))) 经过 concatMap 操作符对数据流进行处理。处理的方法就是 (req) => this.handler.handle(req);实际的请求的发送就是在this.handler.handle(req)方法中。
3、events$.pipe(filter((event) => event instanceof HttpResponse)) 过滤数据流中的 HttpResponse 数据。
完成以上步骤后,request 方法返回操作符处理后的 Observable。因为返回的是 单播的 Cold Observable,所以只有在 subscribe 的时候才会开启 Observable 的数据通知;发出数据通知后才会调用 concatMap 中的处理方法,才会发起实际的HTTP请求。所以只有在 Observable.subscribe() 方法执行的时候,浏览器才会发起HTTP请求。
4、handle() 方法跟踪
上文已指出实际的请求是在 this.handler.handle(req) 方法中完成的,实际上在 handle() 方法中不仅只是发起请求。
查看代码找到 this.handler 定义,先看声明:
export declare class HttpClient {
private handler;
constructor(handler: HttpHandler);
...
}
再看源代码:
class HttpClient {
constructor(handler) {
this.handler = handler;
}
...
}
可以发现 this.handler 为一个 HttpHandler 对象。
查看声明可以发现 HttpHandler 是一个抽象类:
/**
* Transforms an `HttpRequest` into a stream of `HttpEvent`s, one of which will likely be a
* `HttpResponse`.
*
* `HttpHandler` is injectable. When injected, the handler instance dispatches requests to the
* first interceptor in the chain, which dispatches to the second, etc, eventually reaching the
* `HttpBackend`.
*
* In an `HttpInterceptor`, the `HttpHandler` parameter is the next interceptor in the chain.
*
* @publicApi
*/
export declare abstract class HttpHandler {
abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
}
百度翻译一下注释:将一个 “Http 请求” 转换为 “HttpEvent” 事件流,事件流中可能就存在“HttpResponse”。HttpHandler 是可注入的。 注入时,处理程序实例将请求分派到链中的第一个拦截器,然后分派到第二个拦截器,第三个拦截器,依次分派,最终到达 HttpBackend。在 HttpInterceptor 中,HttpHandler 参数是链中的下一个拦截器。
通过注释可以发现 HttpHandler 通过 handle() 方法对请求进行了一个链式的处理流程:主要包括不定数量的拦截器的处理加上最后的 HttpBackend 的处理。
查看代码,找到 HttpHandler 的实现类:
/**
* An injectable `HttpHandler` that applies multiple interceptors
* to a request before passing it to the given `HttpBackend`.
*
* The interceptors are loaded lazily from the injector, to allow
* interceptors to themselves inject classes depending indirectly
* on `HttpInterceptingHandler` itself.
* @see `HttpInterceptor`
*/
export declare class ɵHttpInterceptingHandler implements HttpHandler {
private backend;
private injector;
private chain;
constructor(backend: HttpBackend, injector: Injector);
handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
}
百度翻译一下注释:一个可注入的 HttpHandler,在将请求传递给给定的 HttpBackend 之前,将多个拦截器应用于请求。拦截器从注入器延迟加载,以允许拦截器自己注入类,这间接依赖于 HttpInterceptingHandler 本身。
查看源代码,发现 ɵHttpInterceptingHandler 只是 HttpInterceptingHandler 的别名:
export {
...
HttpBackend, HttpHandler, HttpClient, HttpHeaders,
HTTP_INTERCEPTORS, JsonpClientBackend, JsonpInterceptor,
...
HttpInterceptingHandler as ɵHttpInterceptingHandler,
...
};
找到 HttpInterceptingHandler 的源代码:
class HttpInterceptingHandler {
/**
* @param {?} backend
* @param {?} injector
*/
constructor(backend, injector) {
this.backend = backend;
this.injector = injector;
this.chain = null;
}
/**
* @param {?} req
* @return {?}
*/
handle(req) {
if (this.chain === null) {
/** @type {?} */
const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
this.chain = interceptors.reduceRight((/**
* @param {?} next
* @param {?} interceptor
* @return {?}
*/
(next, interceptor) => new HttpInterceptorHandler(next, interceptor)), this.backend);
}
return this.chain.handle(req);
}
}
从handle() 方法的代码中可以看出,的确如上述注释描述的那样:
通过 const interceptors = this.injector.get(HTTP_INTERCEPTORS, []); 获取模块中注册使用的拦截器。
通过 reduceRight() 方法,将 拦截器和 HttpBackend 组成一个链式结构,对请求进行处理。
5、HttpBackend 介绍
拦截器(HttpInterceptor)的介绍请参考:Angular请求拦截器HttpInterceptor详解
这里直接看链式处理流程的最后一个环节,HttpBackend 的处理。先看 HttpBackend 的声明:
/**
* A final `HttpHandler` which will dispatch the request via browser HTTP APIs to a backend.
*
* Interceptors sit between the `HttpClient` interface and the `HttpBackend`.
*
* When injected, `HttpBackend` dispatches requests directly to the backend, without going
* through the interceptor chain.
*
* @publicApi
*/
export declare abstract class HttpBackend implements HttpHandler {
abstract handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
}
注释说明 HttpBackend 就是最终将请求通过浏览器HTTP API 发送到后端 的 处理程序。拦截器位于 HttpClient 和 HttpBackend 之间,HttpBackend 直接发送请求到后端,不会再经过拦截器的链式处理流程。这里的注释进一步印证了前述的流程说明。
HttpBackend 也是一个抽象类,查找 HttpBackend 的实现类,会发现有两个,一个是 HttpXhrBackend,另一个是 JsonpClientBackend, 通过代码注释可以知道 HttpXhrBackend 是使用 XMLHttpRequest 发送请求的,而 JsonpClientBackend 是使用 JSONP 方法发送请求的。
而 HttpClientModule 中使用的是 HttpXhrBackend :
我们看 HttpXhrBackend 的声明:
/**
* Uses `XMLHttpRequest` to send requests to a backend server.
* @see `HttpHandler`
* @see `JsonpClientBackend`
*
* @publicApi
*/
export declare class HttpXhrBackend implements HttpBackend {
private xhrFactory;
constructor(xhrFactory: XhrFactory);
/**
* Processes a request and returns a stream of response events.
* @param req The request object.
* @returns An observable of the response events.
*/
handle(req: HttpRequest<any>): Observable<HttpEvent<any>>;
}
再看 HttpXhrBackend 的源代码:
handle(req) {
......
return new Observable((
(observer) => {
const xhr = this.xhrFactory.build();
xhr.open(req.method, req.urlWithParams);
......
const partialFromXhr = (() => { ... });
const onLoad = (() => { ... });
const onError = ( (error) => { ... });
const onDownProgress = ( (event) => {
...
observer.next(progressEvent);
});
const onUpProgress = ( (event) => {
...
observer.next(progressEvent);
});
xhr.addEventListener('load', onLoad);
xhr.addEventListener('error', onError);
if (req.reportProgress) {
// Download progress is always enabled if requested.
xhr.addEventListener('progress', onDownProgress);
// Upload progress depends on whether there is a body to upload.
if (reqBody !== null && xhr.upload) {
xhr.upload.addEventListener('progress', onUpProgress);
}
}
// Fire the request, and notify the event stream that it was fired.
xhr.send((/** @type {?} */ (reqBody))); // 实际发出请求的代码
observer.next({ type: HttpEventType.Sent });
return (
() => {
// On a cancellation, remove all registered event listeners.
xhr.removeEventListener('error', onError);
xhr.removeEventListener('load', onLoad);
if (req.reportProgress) {
xhr.removeEventListener('progress', onDownProgress);
if (reqBody !== null && xhr.upload) {
xhr.upload.removeEventListener('progress', onUpProgress);
}
}
// Finally, abort the in-flight request.
xhr.abort();
});
}));
}
其中 const xhr = this.xhrFactory.build(); xhr 为 XMLHttpRequest,对应源代码:
/**
* A factory for `HttpXhrBackend` that uses the `XMLHttpRequest` browser API.
*
*/
class BrowserXhr {
constructor() { }
/**
* @return {?}
*/
build() { return (/** @type {?} */ ((new XMLHttpRequest()))); }
}
使用 JSONP 方法发送请求时使用的 HttpBackend 是 JsonpClientBackend,想要使用JSONP 发送请求需要导入 HttpClientJsonpModule,请参考:【Angular中的HTTP请求】- JSONP 详解
6、总结
综上, HttpClient 就是集成了 RxJS 和 XMLHttpRequest 的封装;所有的请求都是通过 HttpClient.request() 进行调用,方法返回 Cold Observable, 在 Observable 被订阅的时候,请求先经过拦截器的处理,最后通过浏览器 HTTP API 发出。
不同版本的Angular,以上代码位置行数不同,但是文件位置都是一样的,大家可以通过以上关键字搜索就能找到相关代码。
类的声明文件路径:文章来源:https://www.toymoban.com/news/detail-475995.html
node_modules/@angular/common/http/http.d.ts
代码源文件路径:文章来源地址https://www.toymoban.com/news/detail-475995.html
node_modules/@angular/common/fesm2015/http.js
到了这里,关于【Angular中的HTTP请求】- HttpClient 详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!