HttpClient

La plupart des applications frontales communiquent avec des services principaux via le protocole HTTP. Les navigateurs modernes prennent en charge deux API différentes pour les requêtes HTTP: l'interface XMLHttpRequest et l'API fetch().

HttpClient dans @angular/common/http offre une API HTTP client simplifiée pour les applications Angular qui repose sur l'interface XMLHttpRequest exposée par les navigateurs. Les avantages supplémentaires de HttpClient sont des fonctionnalités de testabilité, des objets de requête et de réponse typés, de l'interception des requêtes et des réponses, des API Observable et de la gestion simplifiée des erreurs.

Installation

Avant de pouvoir utiliser le HttpClient, vous devez importer le module Angular HttpClientModule. La plupart des applications le font à la racine AppModule.


Une fois importés HttpClientModule dans le AppModule, vous pouvez injecter le HttpClient dans une classe d'application comme illustré dans l'exemple suivant ConfigService.


Obtenir des données JSON

Les applications demandent souvent des données JSON au serveur. Par exemple, l'application peut avoir besoin d'un fichier de configuration sur le serveur, config.json spécifiant les URL de ressources.

Le ConfigService récupère ce fichier avec une méthode get() sur HttpClient.

Un composant, tel que ConfigComponent, injecte le ConfigService et appelle la méthode getConfig() de service.

Étant donné que la méthode de service renvoie des données Observable de configuration, le composant s'abonne à la valeur de retour de la méthode. Le rappel d'abonnement copie les champs de données dans l'objet config du composant, qui est lié aux données dans le modèle de composant pour l'affichage.

Pourquoi écrire un service

Cet exemple est si simple qu'il est tentant d'écrire Http.get() à l'intérieur du composant et de sauter le service.

Cependant, l'accès aux données reste rarement aussi simple. Généralement, vous post-traitez les données, ajoutez le traitement des erreurs, et éventuellement une logique de nouvelle tentative pour gérer la connectivité intermittente.

Le composant devient rapidement encombré de minuties d'accès aux données. Le composant devient plus difficile à comprendre et à tester, et la logique d'accès aux données ne peut pas être réutilisée ni normalisée.

C'est pourquoi il est recommandé de séparer la présentation des données de l'accès aux données en encapsulant l'accès aux données dans un service séparé et en déléguant à ce service dans le composant, même dans des cas simples comme celui-ci.

Vérification de la réponse par type

Le rappel d'abonnement ci-dessus nécessite une notation entre crochets pour extraire les valeurs de données.

Vous ne pouvez pas écrire data.heroesUrl car TypeScript se plaint que l'objet data du service ne possède pas depropriété heroesUrl.

La méthode HttpClient.get() a analysé la réponse du serveur JSON dans le type Object anonyme. Il ne sait pas quelle est la forme de cet objet.

Vous pouvez indiquer le type de réponse à HttpClient pour rendre la consommation de la sortie plus facile et plus évidente. Tout d'abord, définissez une interface :
Ensuite, spécifiez cette interface en tant que paramètre de HttpClient.get() :

Le rappel dans la méthode du composant mis à jour reçoit un objet de données typé, qui est plus facile et plus sûr à consommer:

Lire la réponse complète

Le corps de la réponse ne renvoie pas toutes les données dont vous pourriez avoir besoin. Parfois, les serveurs renvoient des en-têtes ou des codes d'état spéciaux pour indiquer certaines conditions importantes pour le flux de travail de l'application.

Dites à HttpClient que vous voulez la réponse complète avec l'option observe :

HttpClient.get() retourne maintenant un type Observable de données HttpResponse plutôt que seulement les données JSON. La méthode showConfigResponse() du composant affiche les en-têtes de réponse ainsi que la configuration :

Comme vous pouvez le constater, l'objet de réponse a une propriété body du type correct.

Gestion des erreurs

Que se passe-t-il si la requête échoue sur le serveur ou si une mauvaise connexion réseau l'empêche d'atteindre le serveur ? HttpClientre tourne un objet d'erreur au lieu d'une réponse réussie.

Vous pouvez gérer le composant en ajoutant un deuxième rappel à subscribe().

C'est certainement une bonne idée de donner à l'utilisateur une sorte de retour d'information en cas d'échec de l'accès aux données. Mais afficher l'objet d'erreur brut renvoyé par HttpClientest est loin d'être la meilleure façon de le faire.

Obtenir des détails d'erreur

Détecter qu'une erreur s'est produite est une chose. Interpréter cette erreur et composer une réponse conviviale est un peu plus compliqué.

Deux types d'erreur peuvent se produire. Le serveur backend peut rejeter la demande en renvoyant une réponse HTTP avec un code d'état tel que 404 ou 500.
Il se peut également que quelque chose ne se passe pas du côté client, tel qu'une erreur réseau empêchant la requête d'aboutir ou une exception est levée dans un opérateur RxJS. Ces erreurs produisent des ErrorEventobjets JavaScript.

ErrorEventobjets capture les deux types d'erreurs HttpClient et HttpErrorResponse que vous pouvez consulter pour comprendre ce qui s'est réellement passé.

L'inspection, l'interprétation et la résolution des erreurs sont quelque chose que vous voulez faire dans le service, pas dans le composant.

Vous pouvez d'abord concevoir un gestionnaire d'erreur tel que celui-ci :

Notez que ce gestionnaire renvoie un RxJS ErrorObservable avec un message d'erreur convivial. Les consommateurs du service s'attendent à ce que les méthodes de service renvoient un Observable type, même "mauvais".

Vous récupérez maintenant le résultat Observables renvoyé par les méthodes HttpClient et les transmettez au gestionnaire d'erreur.


retry()

Parfois, l'erreur est transitoire et disparaîtra automatiquement si vous réessayez. Par exemple, les interruptions de réseau sont courantes dans les scénarios mobiles, et une nouvelle tentative peut produire un résultat positif.

La bibliothèque RxJS offre plusieurs nouvelles tentatives des opérateurs qui méritent d'être explorées. Le plus simple est appelé retry() et il s'abonne automatiquement à un échec Observable un nombre de fois spécifié. Ré-abonner au résultat d'un appel HttpClient de méthode a pour effet de réémettre la requête HTTP.

Dirigez-le sur le HttpClient résultat de la méthode juste avant le gestionnaire d'erreurs.


Observables et opérateurs

RxJS est une bibliothèque permettant de composer du code asynchrone et basç sur le rappel dans un style fonctionnel et réactif. De nombreuses API Angular, notamment HttpClient, produisent et consomment RxJS Observables.

RxJS lui-même est hors de portée pour ce guide. Vous trouverez de nombreuses ressources d'apprentissage sur le Web. Bien que vous puissiez vous en tirer avec un minimum de connaissances sur RxJS, vous souhaiterez développer vos compétences sur RxJS au fil du temps afin de les utiliser HttpClient efficacement.

Si vous suivez ces extraits de code, notez que vous devez importer l'observable RxJS et les symboles d'opérateur qui apparaissent dans ces extraits. Ces ConfigService importations sont typiques.


Demande de données non-JSON

Toutes les API ne renvoient pas de données JSON. Dans cet exemple, une méthode DownloaderService lit un fichier texte sur le serveur et enregistre le contenu, avant de renvoyer ce contenu à l'appelant en tant que Observable ‹string›.

HttpClient.get() renvoie une chaîne plutôt que le JSON par défaut en raison de l'option responseType.

RxJS (comme dans "wiretap") permet au code d'inspecter les valeurs correctes et d'erreur passant dans l'observable sans les perturber.

Une méthode download() dans le DownloaderComponent initie la demande en souscrivant à la méthode de service.


Envoi de données au serveur

Outre la récupération des données du serveur, HttpClient prend en charge les demandes de mutation, c'est-à-dire l'envoi de données au serveur avec d'autres méthodes HTTP telles que PUT, POST et DELETE.

Ajout d'en-têtes

De nombreux serveurs nécessitent des en-têtes supplémentaires pour les opérations de sauvegarde. Par exemple, ils peuvent avoir besoin d'un en-tête "Content-Type" pour déclarer explicitement le type MIME du corps de la demande. Ou peut-être que le serveur nécessite un jeton d'autorisation.

Le HeroesService définit de tels en-têtes dans un objet httpOptions qui sera passé à chaque méthode HttpClient de sauvegarde.

Faire une demande POST

Les applications envoient souvent des données à un serveur. Ils postent lors de la soumission d'un formulaire. Dans l'exemple suivant, les publications HeroesService lors de l'ajout d'un héros à la base de données.

La méthode HttpClient.post() est similaire à get(), en ce sens qu'elle a un paramètre de type et qu'elle utilise une URL de ressource.

Il faut deux paramètres supplémentaires :
  • hero : les données à POST dans le corps de la demande.
  • httpOptions : les options de méthode qui, dans ce cas, spécifient les en-têtes requis .
Bien sûr, il détecte les erreurs d'une manière très similaire à celle décrite ci-dessus. Le HeroesComponent initie l'opération POST réelle en vous inscrivant à la Observable retournée par cette méthode de service.

Lorsque le serveur répond avec succès au héros récemment ajouté, le composant l'ajoute à la heroes liste affichée.

Faire une demande DELETE

Cette application supprime un héros avec la méthode HttpClient.delete en transmettant l'identifiant du héros dans l'URL de la demande.

Les HeroesComponent initiés l'opération SUPPRIMER réelle en vous inscrivant à la Observable retournée par cette méthode de service.

Le composant n'attend pas de résultat de l'opération de suppression. Il s'abonne donc sans rappel. Même si vous n'utilisez pas le résultat, vous devez toujours vous abonner. L'appel de la méthode subscribe() exécute l'observable, c'est-à-dire ce qui lance la demande DELETE.

Une méthode HttpClient ne commence pas sa demande HTTP jusqu'à ce que vous appeliez subscribe() l'observable renvoyé par cette méthode. Ceci est vrai pour toutes les méthodes HttpClient.

Touts les observables renvoyées par les HttpClient méthodes sont froides par conception. L'exécution de la requête HTTP est différée, ce qui vous permet d'étendre l'observable avec des opérations supplémentaires telles que tapet catchError avant que quoi que ce soit ne se produise réellement.

L'appel subscribe() déclenche l'exécution de l'observable et entraîne le HttpClient composition et l'envoi de la requête HTTP au serveur.

Vous pouvez considérer ces observables comme des plans pour les requêtes HTTP réelles.

En fait, chaque subscribe() lance une exécution distincte et indépendante de l'observable. S'abonner deux fois entraîne deux demandes HTTP.


Faire une demande PUT

Une application enverra une demande PUT pour remplacer complètement une ressource par des données mises à jour. Lexemple HeroesService suivant ressemble à l'exemple POST.

Pour les raisons expliquées ci-dessus , l'appelant (HeroesComponent.update() dans ce cas) doit voir l'observable renvoyé par le HttpClient.put() pour lancer la demande.

Utilisation avancée

Nous avons discuté de la fonctionnalité HTTP de base dans @angular/common/http, mais il faut parfois faire plus que de simples requêtes et récupérer des données.

Configurer la demande

D'autres aspects d'une requête sortante peuvent être configurés via l'objet options transmis en dernier argument à la méthode HttpClient.

Vous avez vu précédemment que le HeroesService définit les en-têtes par défaut en transmettant un objet options à ses méthodes de sauvegarde.

Mettre à jour les en-têtes

Vous ne pouvez pas modifier directement les en-têtes existants dans l'objet options précédent car les instances de la classe HttpHeaders sont immuables.
Utilisez la méthode set() à la place. Il retourne un clone de l'instance actuelle avec les nouvelles modifications appliquées.

Voici comment vous pouvez mettre à jour l'en-tête d'autorisation avant de faire la demande suivante.

Paramètres de recherche d'URL (URL Parameters)

L'ajout de paramètres de recherche d'URL fonctionne de manière similaire. Voici une méthode searchHeroes qui interroge les héros dont le nom contient le terme recherché.

S'il existe un terme de recherche, le code construit un objet options avec un paramètre de recherche codé en URL HTML. Si le terme était "foo", l'URL de la requête GET serait api/heroes/?name=foo.

Les HttpParams sont immuables, vous devrez donc utiliser la méthode set() pour mettre à jour les options.

Demandes de débordement

L'exemple inclut une fonctionnalité de recherche de paquet npm. Lorsque l'utilisateur entre un nom dans une zone de recherche, il envoie PackageSearchComponent une demande de recherche pour un package portant ce nom à l'API Web NPM.

Voici un extrait pertinent du modèle:

La liaison d'événement (keyup) envoie chaque frappe à la méthode search() du composant.

L'envoi d'une demande pour chaque frappe peut coûter cher. Il est préférable d'attendre que l'utilisateur arrête de taper, puis envoie une demande. C'est facile à mettre en oeuvre avec les opérateurs RxJS, comme le montre cet extrait.

La searchText$ est la séquence des valeurs de la boîte de recherche provenant de l'utilisateur. Il est défini comme un RxJS Subject, ce qui signifie qu'il s'agit d'une multidiffusion Observable qui peut également produire des valeurs pour elle-même en appelant next(value), comme cela se produit dans la méthode search().

Plutôt que de transmettre chaque valeur searchText directement à l'injecté PackageSearchService, le code dans les pipeq ngOnInit() recherche des valeurs à travers trois opérateurs:

  • debounceTime(500) : attendez que l'utilisateur arrête de taper (1/2 seconde dans ce cas).
  • distinctUntilChanged() : attendez que le texte de recherche change.
  • switchMap() : envoyer la demande de recherche au service.
Le code définit packages$, cette nouvelle composition Observablede résultats de recherche. Le modèle s'abonne à AsyncPipe packages$l et affiche les résultats de la recherche au fur et à mesure de leur arrivée.

Une valeur de recherche n'atteint le service que s'il s'agit d'une nouvelle valeur et que l'utilisateur a cessé de taper.

switchMap()

L'opérateur switchMap() a trois caractéristiques importantes.

Il faut un argument de fonction qui retourne un Observable. PackageSearchService.search renvoie un Observable, comme le font les autres méthodes de service de données.

Si une demande de recherche précédente est toujours en vol (comme lorsque la connexion est mauvaise), il annule cette demande et en envoie une nouvelle.

Il renvoie les réponses de service dans leur ordre de demande d'origine, même si le serveur les renvoie dans le désordre.

Si vous pensez réutiliser cette logique anti-rebond, envisagez de la déplacer vers une fonction utilitaire ou dans la fonction PackageSearchService elle-même.


Interception de demandes et de réponses

L'interception HTTP est une caractéristique majeure de @angular/common/http. Avec l'interception, vous déclarez les intercepteurs qui inspectent et transforment les demandes HTTP de votre application vers le serveur. Les mêmes intercepteurs peuvent également inspecter et transformer les réponses du serveur sur le chemin du retour à l'application. Les intercepteurs multiples forment une chaîne en avant et en arrière de gestionnaires de demandes / réponses.

Les intercepteurs peuvent effectuer diverses tâches implicites, de l'authentification à la journalisation, de manière standard et courante pour chaque requête / réponse HTTP.

Sans interception, les développeurs devraient implémenter ces tâches explicitement pour chaque appel de méthode de HttpClient.

Écrire un intercepteur

Pour implémenter un intercepteur, déclarez une classe qui implémente la méthode intercept() de l'interface HttpInterceptor.

Voici un intercepteur Noop qui ne fait rien et qui passe simplement la requête sans la toucher:

La méthode intercept transforme une requête en une requête Observable qui retourne éventuellement la réponse HTTP. En ce sens, chaque intercepteur est tout à fait capable de traiter la demande entièrement par lui-même.

La plupart des intercepteurs inspectent la requête à l'entrée et la transmettent (éventuellement modifiée) à la méthode handle() de l'objet next qui implémente l'interface HttpHandler.

Comme intercept(), la méthode handle() transforme une requête HTTP dans un Observablede HttpEvents qui comprennent finalement la réponse du serveur. La méthode intercept() pourrait inspecter cet observable et le modifier avant de le renvoyer à l'appelant.

Ce no-op intercepteur appelle simplement next.handle() à la demande initiale et renvoie l'observable sans faire une chose.

L'objet next

L'objet next représente le prochain intercepteur de la chaîne d'intercepteurs. Le dernier membre next de la chaîne est le gestionnaire HttpClient dorsal qui envoie la demande au serveur et reçoit la réponse du serveur.

La plupart des intercepteurs appellent next.handle() pour que la demande soit acheminée vers l'intercepteur suivant et, éventuellement, vers le gestionnaire d'arrière-plan. Un intercepteur peut ignorer l'appel next.handle(), court-circuiter la chaîne et renvoyer le sien, Observable avec une réponse de serveur artificielle.

Il s'agit d'un modèle d'intergiciel commun utilisé dans des infrastructures telles que Express.js.

Fournir l'intercepteur

Le Noop Interceptor est un service géré par le système d'injection de dépendance (DI) d'Angular. Comme pour les autres services, vous devez fournir la classe d'intercepteur avant que l'application puisse l'utiliser.

Les intercepteurs étant des dépendances du service HttpClient, vous devez les fournir dans le même injecteur (ou un parent de l'injecteur) fourni HttpClient. Les intercepteurs fournis après la création de DI HttpClient sont ignorés.

Cette application fournit HttpClient dans l'injecteur de racine de l'application, comme un effet secondaire d'importer le HttpClientModule dans AppModule. Vous devez également fournir des intercepteurs AppModule.

Après avoir importé le jeton d'injection depuis HTTP_INTERCEPTORS, écrivez le fournisseur comme suit : @angular/common/httpNoopInterceptor

Notez l'option multi : true. Ce paramètre requis indique à Angular qu'il s'agit d'un jeton HTTP_INTERCEPTORS pour un fournisseur multiple qui injecte un tableau de valeurs plutôt qu'une valeur unique.

Vous pouvez ajouter ce fournisseur directement au tableau de fournisseurs du AppModule. Cependant, c'est plutôt bavard et il y a de bonnes chances que vous créiez plus d'intercepteurs et que vous les fournissiez de la même manière. Vous devez également porter une attention particulière à l'ordre dans lequel vous fournissez ces intercepteurs.

Envisagez de créer un fichier "barrel" regroupant tous les fournisseurs d'intercepteurs dans un tableau httpInterceptorProviders, en commençant par celui-ci NoopInterceptor.

Puis importez et ajoutez-le au tableau AppModule de fournisseurs comme ceci:

Lorsque vous créez de nouveaux intercepteurs, ajoutez-les au tableau httpInterceptorProviders et vous n'aurez plus à revenir sur le tableau AppModule.

Commande intercepteur

Angular applique les intercepteurs dans l'ordre dans lequel vous les fournissez. Si vous fournissez des intercepteurs A , puis B , puis C , les demandes coulera dans A > B > C et réponses couleront C > B -> A .

Vous ne pouvez pas modifier l'ordre ou supprimer les intercepteurs ultérieurement. Si vous devez activer et désactiver un intercepteur de manière dynamique, vous devez intégrer cette fonctionnalité dans l'intercepteur lui-même.

HttpEvents

Vous vous êtes peut-être attendu à ce que les méthodes handle() renvoient des éléments observables, HttpResponse ‹any› comme la plupart des méthodes HttpClient.

Au lieu de cela, ils retournent des observables de HttpEvent ‹any›.

En effet, les intercepteurs fonctionnent à un niveau inférieur à celui de ces méthodes HttpClient. Une seule requête HTTP peut générer plusieurs événements, y compris des événements de progression de téléchargement et de téléchargement. La classe HttpResponse elle-même est en réalité un événement dont le type est HttpEventType.HttpResponseEvent.

De nombreux intercepteurs ne sont concernés que par la demande sortante et renvoient simplement le flux d'événements next.handle() sans le modifier.

Mais les intercepteurs qui examinent et modifient la réponse next.handle() verront tous ces événements. Votre intercepteur devrait laisser chaque événement intact, sauf s'il a une raison impérieuse de faire autrement .

Immutabilité

Bien que les intercepteurs soient capables de muter les demandes et les réponses, les propriétés d'instance HttpRequest et HttpResponse sont les readonly rendant largement immuables.

Ils sont immuables pour une bonne raison: l'application peut réessayer une demande plusieurs fois avant qu'elle ne réussisse, ce qui signifie que la chaîne d'intercepteur peut retraiter la même demande plusieurs fois. Si un intercepteur pouvait modifier l'objet de demande d'origine, l'opération réessayée commencerait par la demande modifiée plutôt que par l'original. L'immuabilité garantit que les intercepteurs voient la même demande pour chaque essai.

TypeScript vous empêchera de définir des propriétés HttpRequest en lecture seule.

Pour modifier la requête, clonez-la d'abord et modifiez le clone avant de le transmettre à next.handle(). Vous pouvez cloner et modifier la demande en une seule étape, comme dans cet exemple.

L'argument clone() de la méthode vous permet de muter des propriétés spécifiques de la demande tout en copiant les autres.

Le corps de la demande

Le readonly, garde d'affectation ne peut pas empêcher les mises à jour profondes et, en particulier, il ne vous empêche pas de modifier une propriété d'un objet de corps de requête.

Si vous devez modifier le corps de la demande, copiez-le d'abord, modifiez la copie et la demande clone(), puis définissez le corps du clone avec le nouveau corps, comme dans l'exemple suivant.

Effacement du corps de la demande

Parfois, vous devez vider le corps de la demande plutôt que de le remplacer. Si vous définissez le corps de la demande clonée sur undefined, Angular suppose que vous avez l'intention de quitter le corps tel quel. Ce n'est pas ce que vous voulez. Si vous définissez le corps de la demande clonée sur null, Angular sait que vous souhaitez effacer le corps de la demande.

Définir en-têtes par défaut

Les applications utilisent souvent un intercepteur pour définir les en-têtes par défaut des demandes sortantes.

L'exemple d'application contient un jeton AuthService d'autorisation. Voici son AuthInterceptor qui injecte ce service pour obtenir le jeton et ajoute un en-tête d'autorisation avec ce jeton à chaque demande sortante:

La pratique consistant à cloner une demande pour définir de nouveaux en-têtes est si courante qu'il existe un raccourci setHeaders pour cela:

Un intercepteur qui modifie les en-têtes peut être utilisé pour différentes opérations, notamment:
  • Autorisation d'authentification
  • Comportement de mise en cache; par exemple,If-Modified-Since
  • XSRF protection
Logging

Comme les intercepteurs peuvent traiter la demande et la réponse ensemble, ils peuvent effectuer des opérations telles que l'heure et consigner une opération HTTP complète.

Considérez ce qui suit LoggingInterceptor, qui capture le temps de la demande, le temps de la réponse et enregistre le résultat avec le temps écoulé avec l'injecté MessageService.

L'opérateur tap RxJS capture si la requête a réussi ou échoué. L'opérateur finalize RxJS est appelé lorsque la réponse constatée est soit erronée, soit terminée (ce qu'il doit), et il en informe le résultat MessageService.

Mise en cache

Les intercepteurs peuvent gérer les demandes eux-mêmes, sans transfert de données next.handle().

Par exemple, vous pouvez décider de mettre en cache certaines demandes et réponses pour améliorer les performances. Vous pouvez déléguer la mise en cache à un intercepteur sans perturber vos services de données existants.

Le CachingInterceptor démontre cette approche.

La fonction isCachable() détermine si la requête est cachable. Dans cet exemple, seules les demandes GET adressées à l'API de recherche de paquets npm sont cachables.

Si la demande ne peut pas être cachée, l'intercepteur transmet simplement la demande au prochain gestionnaire de la chaîne.

Si une demande cachable est trouvée dans le cache, l'intercepteur renvoie un observable of() avec la réponse en cache, en contournant le gestionnaire next (et tous les autres intercepteurs en aval).

Si une demande cachable n'est pas dans le cache, le code appelle sendRequest.

La fonction sendRequest crée un clone de requête sans en-têtes car l'API npm les interdit.

Il transmet la demande à next.handle() laquelle appelle finalement le serveur et renvoie la réponse du serveur.

Notez comment sendRequest intercepte la réponse avant de retourner à l'application. Il achemine la réponse via l'opérateur tap(), dont le rappel ajoute la réponse au cache.

La réponse d'origine continue à être sauvegardée sans interruption dans la chaîne d'intercepteurs jusqu'à l'appelant de l'application.

Les services de données, tels que PackageSearchService, ne savent pas que certaines de leurs demandes HttpClient renvoient des réponses en cache.

Renvoyer un observable à plusieurs valeurs

La méthode HttpClient.get() retourne normalement un observable qui émet les données ou une erreur. Certaines personnes le décrivent comme un observable "un fait accompli".

Mais un intercepteur peut changer cela en un observable qui émet plus d'une fois.

Une version révisée du document renvoie CachingInterceptor facultativement un observable qui émet immédiatement la réponse mise en cache, envoie quand même la demande à l'API Web NPM et émet à nouveau ultérieurement avec les résultats de recherche mis à jour.

L'option cache-then-refresh est déclenchée par la présence d'un en-tête personnalisé x-refresh

Une case à cocher sur la bascule PackageSearchComponent un drapeau withRefresh, qui est l'un des arguments à PackageSearchService.search(). Cette méthode search() crée l'en-tête x-refresh personnalisé et l'ajoute à la demande avant l'appel HttpClient.get().

La version révisée CachingInterceptor configure une demande de serveur, qu'il y ait ou non une valeur mise en cache, en utilisant la même méthode sendRequest() que celle décrite ci-dessus.
S'il n'y a pas de valeur en cache, l'intercepteur retourne results$.

S'il existe une valeur mise en cache, le code dirige la réponse mise en cache results$, produisant un observable recomposé qui émet deux fois, la réponse mise en cache en premier (et immédiatement), suivie de la réponse du serveur. Les abonnés voient une séquence de deux réponses.

Écoute des événements de progrès

Parfois, les applications transfèrent de grandes quantités de données et ces transferts peuvent prendre beaucoup de temps. Les téléchargements de fichiers sont un exemple typique. Donner aux utilisateurs une meilleure expérience en fournissant des informations sur les progrès de ces transferts.

Pour faire une demande avec les événements de progression activés, vous pouvez créer une instance HttpRequest avec l'option reportProgress définie sur true pour permettre le suivi des événements de progression.

Chaque événement de progression déclenche la détection des modifications. Ne les activez donc que si vous avez réellement l'intention de signaler les progrès dans l'interface utilisateur.

Ensuite, passez cet objet de requête à la méthode HttpClient.request(), qui retourne un Observableof HttpEvents, les mêmes événements traités par les intercepteurs:

La méthode getEventMessage interprète chaque type de flux HttpEvent d'événements.

L'exemple d'application de ce guide ne comporte pas de serveur acceptant les fichiers téléchargés. Les interceptions et les court-circuits UploadInterceptor dans app/http-interceptors/upload-interceptor.ts chargent les requêtes en renvoyant un observable d'événements simulés.

Sécurité: Protection XSRF

La falsification de requêtes inter-sites (XSRF) est une technique d'attaque par laquelle l'attaquant peut amener un utilisateur authentifié à exécuter inconsciemment des actions sur votre site Web. HttpClient prend en charge un mécanisme commun utilisé pour prévenir les attaques XSRF. Lors de l'exécution de requêtes HTTP, un intercepteur lit par défaut un jeton dans un cookie XSRF-TOKEN et le définit comme un en-tête HTTP X-XSRF-TOKEN. étant donné que seul le code exécuté sur votre domaine peut lire le cookie, le serveur peut être certain que la requête HTTP provient de votre application cliente et non d'un attaquant.

Par défaut, un intercepteur envoie ce cookie sur toutes les demandes de mutation (POST, etc.) à des URL relatives, mais pas sur les demandes GET / HEAD ni sur les demandes avec une URL absolue.

Pour en tirer parti, votre serveur doit définir un jeton dans un cookie de session lisible en JavaScript appelé XSRF-TOKEN lors du chargement de la page ou de la première demande GET. Lors de demandes ultérieures, le serveur peut vérifier que le cookie correspond à l'en-tête X-XSRF-TOKEN HTTP. Par conséquent, assurez-vous que seul le code exécuté sur votre domaine a pu envoyer la demande. Le jeton doit être unique pour chaque utilisateur et doit être vérifiable par le serveur; Cela empêche le client de créer ses propres jetons. Définissez le jeton sur un condensé du cookie d'authentification de votre site avec un sel pour une sécurité accrue.

Pour éviter les collisions dans les environnements où plusieurs applications Angular partagent le même domaine ou sous-domaine, attribuez à chaque application un nom de cookie unique.

Notez que HttpClient ne prend en charge que la moitié client du schéma de protection XSRF. Votre service principal doit être configuré pour définir le cookie de votre page et pour vérifier que l'en-tête est présent sur toutes les demandes éligibles. Si ce n'est pas le cas, la protection par défaut d'Angular sera inefficace.


Configuration des noms de cookie / en-tête personnalisés

Si votre service principal utilise des noms différents pour le cookie ou l'en-tête de jeton XSRF, utilisez HttpClientXSRFModule.withOptions() pour remplacer les valeurs par défaut.

Test des requêtes HTTP

Comme toute dépendance externe, le backend HTTP doit être simulé afin que vos tests puissent simuler une interaction avec un serveur distant. La bibliothèque facilite la mise en place de tels Mock @angular/common/http/testing

Mocking

La bibliothèque de tests HTTP d'Angular est conçue pour un modèle de tests dans lequel l'application exécute le code et effectue les demandes en premier. Ensuite, un test s'attend à ce que certaines demandes aient été faites ou non, effectue des assertions à l'encontre de ces demandes et fournit enfin des réponses en "purgeant" chaque demande attendue. Á la fin, les tests peuvent vérifier que l'application n'a pas effectué de demandes inattendues.

Installer

Pour commencer à tester les appels sur HttpClient, importez les HttpClientTestingModule contrôleurs Mocking et Mocking HttpTestingController, ainsi que les autres symboles requis par vos tests.

Ajoutez ensuite le HttpClientTestingModule à TestBed et poursuivez la configuration du service sous test.

Désormais, les demandes effectuées au cours de vos tests atteindront le backend de test au lieu du backend normal.

Cette configuration appelle également TestBed.get() à injecter le service HttpClient et le contrôleur mocking afin qu'ils puissent être référencés lors des tests.

Attente et réponse aux demandes

Vous pouvez maintenant écrire un test qui s'attend à une requête GET et fournit une réponse fictive.
La dernière étape, vérifiant qu'aucune demande n'est restée en attente, est assez commune pour que vous puissiez la déplacer dans une étape afterEach() :
Attentes de la demande personnalisée

Si la correspondance par URL n'est pas suffisante, il est possible d'implémenter votre propre fonction de correspondance. Par exemple, vous pouvez rechercher une demande sortante ayant un en-tête d'autorisation :

Comme dans le cas précédent expectOne(), le test échouera si 0 ou plus de 2 requêtes satisfont à ce prédicat.

Gestion de plusieurs demande

Si vous devez répondre à des demandes en double dans votre test, utilisez l'API match() au lieu de expectOne(). Il prend les mêmes arguments mais renvoie un tableau de requêtes correspondantes. Une fois renvoyées, ces demandes sont supprimées de la correspondance ultérieure et vous êtes responsable de leur vidage et de leur vérification.

Tester les erreurs

Vous devez tester les défenses de l'application contre les requêtes HTTP qui échouent.

Appelez request.flush() avec un message d'erreur, comme dans l'exemple suivant.

Alternativement, vous pouvez appeler request.error() avec un ErrorEvent.