Renvoyer les données réelles du catalogue

GolangBeginner
Pratiquer maintenant

Introduction

Un bureau de quartier possède une petite collection d’objets trouvés. Sa page préparée peut afficher les objets, mais l’API d’exemple ne renvoie qu’une fiche. Vous relierez de véritables fiches Go aux réponses de liste et de détail afin que les visiteurs trouvent le bon lieu de retrait.

Vous devez déjà connaître les fonctions, structures, slices, maps et modules de Go, ainsi que les chemins de base du terminal. HTTP, JSON et les tests de gestionnaires sont présentés ici. L’environnement accessible dans le navigateur comprend Go et une page prête à l’emploi. Cette unité démarre indépendamment et ne nécessite aucun fichier d’un autre laboratoire. Prévoyez environ 55 minutes, lecture et erreurs courantes comprises ; il s’agit d’une estimation de l’auteur.

Sélectionner un objet sans perturber la liste

Dans cette étape, conservez une collection fonctionnelle à une carte tout en ajoutant les véritables détails des objets. Commencez dans l’onglet Workspace. Son shell entre dans /home/labex/project/lost-found ; le cd suivant rétablit cet emplacement si vous revenez plus tard.

Go est installé dans /usr/local/go/bin. export ajoute ce répertoire au chemin de recherche des commandes du shell, en conservant le $PATH existant. Cela s’applique à la session de terminal actuelle ; répétez-le dans un nouveau shell. Aucun réglage global du shell ne change.

cd /home/labex/project/lost-found
export PATH=/usr/local/go/bin:$PATH

curl envoie une requête. -i inclut les en-têtes de réponse, et les apostrophes gardent l’URL en un seul argument. GET demande la lecture d’une ressource. /api/items désigne la collection ; 127.0.0.1:8080 correspond au service sur cette même machine. Utilisez d’abord l’exemple normal :

curl -i 'http://127.0.0.1:8080/api/items'

Attendez-vous à 200 OK, à un en-tête Content-Type: application/json et à un tableau contenant LF-101, le parapluie bleu de la bibliothèque. Le statut HTTP décrit le résultat de la requête ; le corps transporte les données. JSON utilise des crochets pour les tableaux et des accolades pour les objets. Un 200 seul ne prouve pas que la collection est complète. Passez à Lost and Found pour voir son unique carte, puis revenez à Workspace.

cat affiche un fichier sans le modifier. Lisez les fiches sources, la structure d’entrée et votre petit gestionnaire de départ :

cat items.json
cat main.go
cat handler.go

Il existe six fiches. Item regroupe ID, Name, Category et Location ; les champs exportés dont le nom commence par une majuscule peuvent être encodés. Des étiquettes comme `json:"id"` choisissent des clés JSON en minuscules. main.go charge la collection, sert la page préparée et transmet son slice d’objets à catalogHandler. Préservez main.go, go.mod, items.json, serve.py et web/ ; ne modifiez que handler.go et les éventuels fichiers ou tests Go supplémentaires.

Avant de modifier le code, prévoyez la différence entre une requête pour LF-101 et une pour LF-103. Les deux noms sont Blue umbrella, mais leurs lieux sont Library desk et Cafe counter. Quel champ peut sélectionner une seule fiche ? Demandez maintenant le second ID :

curl -i 'http://127.0.0.1:8080/api/items/LF-103'

Le code initial renvoie encore son tableau contenant la première fiche. C’est une réponse reçue, mais pas un détail correct. Votre tâche est de renvoyer l’objet sélectionné par l’ID tout en maintenant la réponse de liste actuelle disponible. Ne résolvez pas cela en renvoyant le premier objet pour tous les chemins.

Un gestionnaire est du code Go appelé pour chaque requête. r *http.Request pointe vers la requête entrante. w http.ResponseWriter est la destination : Header définit les en-têtes et les écritures envoient le corps. http.Handler décrit ce comportement ; http.HandlerFunc y adapte une fonction. La fonction anonyme renvoyée conserve les items fournis. Vous n’avez pas à écrire un analyseur HTTP.

Avant de modifier le code, gardez une copie. cp copie le fichier ; l’extension .backup évite volontairement .go, qui créerait des définitions en double quand Go compile le paquet.

cp handler.go handler.go.backup

Ouvrez le véritable fichier dans l’éditeur du terminal. Le réglage LC_ALL=C.UTF-8, limité à cette commande, fait interpréter le texte comme UTF-8 par nano, y compris les caractères accentués. Il ne concerne que cette invocation de l’éditeur, pas les réglages globaux du shell :

LC_ALL=C.UTF-8 nano handler.go

Dans nano, Ctrl signifie maintenir Control en appuyant sur la touche indiquée. Déplacez-vous avec les flèches. Appuyez sur Ctrl+W, saisissez catalogHandler, puis Entrée pour trouver la fonction ; Ctrl+C annule une recherche inachevée. Modifiez avec la saisie ordinaire, Retour arrière et Suppr. Ctrl+K coupe la ligne actuelle ; ne l’utilisez que sur une ligne à remplacer. Les raccourcis en bas utilisent ^ pour Control. Vous modifiez le gestionnaire existant, sans créer un second programme.

Implémentez les décisions suivantes dans la fonction renvoyée, après son en-tête de type de contenu existant. Travaillez une branche à la fois :

D’abord, lisez r.URL.Path. S’il vaut /api/items, conservez l’encodage du tableau initial items[:1] et revenez immédiatement. Cela préserve la liste normale à une carte pendant votre travail sur les détails.

Ensuite, dans les autres cas, reconnaissez le préfixe /api/items/. Ajoutez l’import de la bibliothèque standard strings à côté des imports existants. strings.HasPrefix(path, prefix) vérifie un préfixe, et strings.TrimPrefix(path, prefix) le retire. Stockez l’ID restant dans une variable locale.

Puis parcourez le slice fourni avec for _, item := range items. Comparez item.ID à l’ID demandé, pas au nom. Pour la correspondance, encodez cet objet item et revenez immédiatement. json.NewEncoder(w).Encode(value) écrit la valeur Go choisie en JSON ; un slice devient un tableau et une structure devient un objet. La boucle doit examiner toutes les fiches, pas seulement items[0].

Après cela, si aucun ID ne correspond ou si le chemin ne relève pas de ces routes, définissez 404 avant d’écrire une erreur JSON. w.WriteHeader(http.StatusNotFound) fixe le statut. Une petite valeur d’erreur compatible avec JSON est map[string]string{"error": "item not found"} ; la chaîne error doit être non vide. Encodez cette map. N’écrivez pas une autre liste après une erreur.

La première écriture d’une réponse valide normalement 200 ; définissez donc tout autre statut avant d’encoder. Encode renvoie un error : nil signifie aucun échec. Dans cet exemple, toutes les valeurs sont des chaînes compatibles avec JSON. Un client déconnecté peut malgré tout faire échouer l’écriture ; envoyer une seconde réponse une fois le corps commencé ne peut pas réparer la connexion. Les retours anticipés empêchent également d’ajouter deux réponses.

Enregistrez avec Ctrl+O, confirmez le nom existant handler.go avec Entrée, puis quittez avec Ctrl+X. Examinez le fichier enregistré :

cat handler.go

Si vous avez ouvert le mauvais fichier, quittez sans enregistrer : Ctrl+X, puis N si l’éditeur demande de sauvegarder. Exécutez pwd pour examiner votre emplacement, rétablissez le premier cd et rouvrez le bon nom de fichier. Si l’invite d’enregistrement affiche un nom inattendu, Ctrl+C l’annule. N’écrasez pas les données sources pour compenser une erreur d’édition.

gofmt -w formate le fichier Go nommé sur place. Le serve.py préparé compile votre code avant de remplacer uniquement le serveur enregistré de cet espace. Il ne fournit aucune implémentation du gestionnaire. Une erreur de compilation conserve le processus fonctionnel précédent ; une ancienne réponse du navigateur ne prouve donc pas que le nouveau code a compilé.

gofmt -w handler.go
python3 serve.py

Attendez-vous à Lost and Found ready on port 8080. Si le formatage ou la compilation échoue, lisez le nom du fichier et le numéro de ligne. Rouvrez nano handler.go, trouvez l’expression voisine avec Ctrl+W, réparez uniquement cette partie, enregistrez, quittez et répétez les deux commandes. Préservez la logique déjà fonctionnelle ; ne restaurez pas la sauvegarde initiale par-dessus les progrès ultérieurs. Si No such file apparaît, rétablissez le premier cd. Les diagnostics du service sont dans .work/server.log, lisible avec cat .work/server.log ; une faute de frappe ne justifie pas de réinitialiser la VM.

curl -i 'http://127.0.0.1:8080/api/items/LF-103'

Attendez-vous à un objet dont id vaut LF-103 et location vaut Cafe counter, pas à un tableau ni au parapluie de la bibliothèque. Vérifiez que votre liste normale renvoie toujours son tableau d’exemple :

curl -i 'http://127.0.0.1:8080/api/items'

Vous avez ajouté la sélection tout en conservant le format de collection de la page fonctionnelle. L’étape suivante étend volontairement cette liste à toutes les fiches.

Renvoyer la collection complète

Dans cette étape, transformez la liste d’une fiche en catalogue complet en lecture seule sans perdre les détails que vous avez écrits. Commencez dans Workspace, dans /home/labex/project/lost-found.

Avant de modifier le code, lisez l’expression de slice dans votre branche de collection. items[:1] sélectionne un préfixe qui s’arrête avant l’indice un ; elle masque donc les cinq autres fiches. Prévoyez la réponse si l’encodeur reçoit items lui-même. Contrairement à un tableau de six objets codé en dur, cet argument fonctionne aussi avec un autre slice fourni.

LC_ALL=C.UTF-8 nano handler.go

Recherchez items[:1] avec Ctrl+W et Entrée. Remplacez uniquement la valeur encodée de la collection par le slice d’entrée complet. Conservez l’en-tête JSON, la boucle de détail, l’erreur d’ID absent et les retours anticipés. Enregistrez sous le même nom avec Ctrl+O et Entrée, puis Ctrl+X.

Une collection vide doit être encodée en [], jamais en null. Un slice vide non nil s’encode en [] ; un slice nil s’encoderait en null. L’entrée vide indépendante de cette unité est []Item{}. Ne remplacez pas un résultat vide par une erreur d’objet absent : une liste et un détail ont des sens différents. L’ordre du tableau et des clés d’objet est libre, mais le nom, la catégorie et le lieu appartenant à chaque ID doivent rester associés.

gofmt -w handler.go
python3 serve.py

Demandez la collection puis les deux parapluies avec leurs ID distincts :

curl -i 'http://127.0.0.1:8080/api/items'
curl -i 'http://127.0.0.1:8080/api/items/LF-101'
curl -i 'http://127.0.0.1:8080/api/items/LF-103'

Attendez-vous aux six fiches complètes dans le tableau. LF-101 correspond toujours à Library desk et LF-103 à Cafe counter, même si les deux noms sont Blue umbrella. Si l’un des détails a changé de lieu, examinez la comparaison d’ID et la valeur de boucle que vous encodez.

La requête suivante utilise volontairement un ID absent. Attendez-vous à HTTP 404 et à un objet JSON contenant une chaîne error non vide, plutôt qu’à une liste vide. curl se termine normalement avec succès pour une réponse d’erreur HTTP reçue : recevoir une réponse diffère d’un échec de connexion.

curl -i 'http://127.0.0.1:8080/api/items/no-such-id'

Ce gestionnaire ne filtre pas encore et n’impose pas la méthode HTTP ; le prochain lab guidé introduit ces comportements. Ne prenez pas une page d’accueil normale ou un 200 quelconque comme preuve d’un point d’accès de détail fonctionnel. Vous avez maintenant préservé ensemble liste et détails en modifiant la portée de la collection.

Écrire un test qui distingue les identités

Dans cette étape, concevez votre propre test ciblé du gestionnaire sans lancer un second serveur actif, puis utilisez la page réelle. Commencez dans Workspace, dans le même répertoire.

Décidez d’abord ce que le test doit détecter : un gestionnaire pourrait renvoyer le premier nom correspondant au lieu de l’ID demandé. Choisissez deux petites fiches personnelles de même nom mais d’ID et de lieux différents, extérieures à la collection de la page. Demandez le second ID. Notez le statut, l’ID et le lieu qui doivent être renvoyés. Ce jeu indépendant fait échouer une réponse réelle fixe et rend le test utile au-delà des six fiches préparées.

Go découvre les tests dans les fichiers terminés par _test.go. Une fonction de test commence par Test et accepte t *testing.T. Créez votre fichier de test avec nano :

LC_ALL=C.UTF-8 nano handler_test.go

Commencez par package main. Importez encoding/json, net/http/httptest et testing. Ajoutez une fonction telle que func TestSecondIdentity(t *testing.T) { ... }, en remplaçant les points par vos instructions de test. Son corps comprend quatre parties :

D’abord, construisez votre jeu de deux fiches []Item avec les noms de champs exportés de main.go. Donnez-le à catalogHandler, sans charger ni modifier items.json.

Ensuite, créez une requête pour le détail de la seconde fiche. httptest.NewRequest("GET", path, nil) construit une requête ; le dernier argument signifie qu’elle n’a pas de corps. httptest.NewRecorder() renvoie un enregistreur de réponse. Appeler le ServeHTTP(recorder, request) de votre gestionnaire exécute le véritable gestionnaire et capture ses en-têtes, son statut et son corps.

Puis déclarez var got Item. Décodez avec json.Unmarshal(recorder.Body.Bytes(), &got). &got est l’adresse où les champs seront stockés. Vérifiez d’abord la valeur error renvoyée : un JSON invalide ne doit pas être pris pour une structure vide valide. Par exemple, if err != nil { t.Fatal(err) } arrête le test avec un échec lisible.

Après cela, vérifiez le statut et l’identité attendus. recorder.Code contient le statut, tandis que got.ID et got.Location contiennent les champs décodés. Comparez-les à la seconde fiche du jeu. Utilisez if et t.Fatalf pour échouer avec les valeurs réelles. %d affiche un nombre et %+v montre les champs de structure. Ne vérifiez pas seulement qu’un JSON existe.

Utilisez vos ID et lieux choisis de façon cohérente dans la requête et les assertions. Gardez les données du jeu, l’exécution et les assertions dans la même fonction de test pour faciliter le suivi du raisonnement. Enregistrez avec Ctrl+O et Entrée, puis Ctrl+X. L’environnement ne fournit pas de test réussi à votre place.

go test -v . exécute les tests de ce paquet : -v affiche les noms des tests et . sélectionne le paquet courant.

gofmt -w handler_test.go
go test -v .

Attendez-vous au nom de votre test suivi de PASS. Un échec doit afficher le statut ou l’objet réel. Si le test échoue, déterminez si la relation attendue dans votre jeu ou le gestionnaire est incorrect ; corrigez uniquement cette partie avec nano et relancez le test. Une erreur de compilation indique un fichier et une ligne à examiner. Recompilez avec python3 serve.py après toute correction du gestionnaire pour que le navigateur utilise aussi le code actuel. Conservez au moins un test Go de gestionnaire réussi dans cet espace ; la vérification finale exécute vos tests isolément et vérifie l’API avec d’autres fiches d’entrée fournies.

Passez à Lost and Found et rechargez. Attendez-vous à 6 items found. Ouvrez View item sur LF-103 et lisez sa référence et son lieu de retrait, puis choisissez Back to collection. Les deux parapluies doivent rester distinguables. Cette unité fournit la navigation dans la collection et les détails ; le prochain lab guidé ajoute les commandes de recherche avec leur filtrage dans l’API.

Page réelle du catalogue contenant toutes les fiches sources

Repérez les six cartes et leurs références individuelles. La page consomme votre API active et ne contient aucune collection de secours. Revenez à Workspace si une correction est nécessaire. Vous avez maintenant un catalogue utilisable et un test dont vous avez choisi le jeu et les assertions pour révéler une véritable erreur d’identité.

Résumé

Vous avez encodé des fiches Go en JSON, sélectionné les réponses de liste et de détail selon le chemin, conservé les noms identiques associés à leurs propres ID et renvoyé un véritable statut pour un objet absent. Vous avez également écrit un test isolé avec httptest et utilisé la page réelle. La recherche et la gestion plus stricte des erreurs constituent la tâche indépendante du prochain laboratoire guidé.