【AngularでのHTTPリクエスト】 - HttpClient 詳細解説

        ほとんどのフロントエンド アプリケーションは、データをダウンロードまたはアップロードし、他のバックエンド サービスにアクセスするために、HTTP プロトコルを介してサーバーと通信します。Angular は、アプリケーション用の HTTP クライアント API を提供します。これは、@angular/common/httpHttpClientサービス クラスです。

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>;

        ここではよく使われるメソッドを 4 つだけ挙げますが、実際にはrequestheadoptionspatchjsonpなどのメソッドがあり、同じメソッド名でオーバーロードされたメソッドが複数あります。実際のニーズに応じて、適切なメソッドをプロジェクトで呼び出すことができます。

要求されたすべてのメソッドの戻り値がObservableオブザーバブル オブジェクト        であることがわかります。

3.通話関係

HttpClientクラスのソース コード        を見ると、すべてのリクエスト メソッドが異なるパラメーターを渡すことによってリクエストメソッドを呼び出していることがわかります。

    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メソッドはoperator によって処理されたObservable を返します。返されるのはユニキャストの Cold Observable であるため、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>>;
}

        Baidu は注記を翻訳します。「Http Request」を「HttpEvent」イベント ストリームに変換すると、イベント ストリームに「HttpResponse」が含まれる場合があります。HttpHandler は注入可能です。注入されると、ハンドラー インスタンスは要求をチェーン内の最初のインターセプターにディスパッチし、次に 2 番目のインターセプターにディスパッチし、さらに 2 番目のインターセプターにディスパッチし、3 番目のインターセプターにディスパッチして、最後に 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>>;
}

        Baidu は注記を翻訳します:リクエストを特定のHttpBackendに渡す前に、複数のインターセプターをリクエストに適用する注入可能なHttpHandler 。インターセプターはインジェクターから遅延ロードされ、インターセプターがクラス自体を注入できるようにします。これは間接的に 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()メソッドのコード        からわかるように、実際には上記のコメントで説明されているとおりです。

モジュールに登録されているインターセプターを取得するには、constinterceptors = this.injector.get(HTTP_INTERCEPTORS, []); を        渡します。

reduceRight()メソッド        を通じて、インターセプターとHttpBackend はチェーン構造を形成してリクエストを処理します。

5. HttpBackend の紹介

        インターセプター ( HttpInterceptor ) の概要については、Angular Request Interceptor 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 を介して最終的にリクエストをバックエンドに送信するハンドラーであることを示しています。インターセプターはHttpClientHttpBackendの間にあり、HttpBackend はインターセプターのチェーン処理プロセスを経由せずにリクエストを直接バックエンドに送信します。ここでのコメントは、前述のフローの説明をさらに裏付けるものです。

        HttpBackendも抽象クラスです. HttpBackendの実装クラスを検索するとHttpXhrBackendJsonpClientBackendの 2 つが見つかります.コードのコメントを見るとHttpXhrBackendがXMLHttpRequestを使用してリクエストを送信しているのに対し、JsonpClientBackend はJSONPを使用していることがわかります.リクエストを送るメソッド。

        そしてHttpXhrBackendはHttpClientModuleで使用されます:

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(); xhrXMLHttpRequestで、ソース コードに対応します。

/**
 * 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 がサブスクライブされると、リクエストは最初にインターセプターによって処理され、最後にbrowser サーバーの HTTP API によって発行されます。

        Angular のバージョンが異なると、上記のコードの場所の行数は異なりますが、ファイルの場所は同じです.上記のキーワードを検索すると、関連するコードを見つけることができます.

        クラス宣言ファイルのパス:

node_modules/@angular/common/http/http.d.ts

        コード ソース ファイルのパス:

node_modules/@angular/common/fesm2015/http.js

おすすめ

転載: blog.csdn.net/evanyanglibo/article/details/122196426