11 - La negociation de contenu : servir le bon format
Ce que tu vas apprendre
- Le fonctionnement du header Accept et des types MIME
- Les facteurs de qualité (q=) et leur mecanique de priorité
- La negociation de langue avec Accept-Language
- Pourquoi le header Vary est critique pour le cache
- La différence entre negociation pilotee par le serveur et par le client
Prerequisites
Tu demandes une page. Le serveur peut te répondre en HTML, en JSON, en XML, en français, en anglais, compresse ou non. Comment se fait le choix ? Par la negociation de contenu. C'est un mecanisme ou le client exprime ses préférences et le serveur fait de son mieux pour les satisfaire.
La beaute du système, c'est que la meme URL peut servir des representations différentes selon le client. Un navigateur recoit du HTML. Un appel curl avec Accept: application/json recoit du JSON. Meme ressource, formats différents.
Accept : choisir le type de contenu
Le header Accept liste les types MIME que le client accepte :
GET /api/users/42 HTTP/1.1
Accept: text/html, application/json, */*
Ce client dit : "je préféré du HTML, j'accepte du JSON, et a défaut je prends n'importe quoi".
Le serveur examine cette liste, compare avec ce qu'il sait produire, et choisit. Sa réponse indique le format choisi via Content-Type :
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Si le serveur ne peut satisfaire aucun des types demandes, il renvoie un 406 Not Acceptable. En pratique, c'est rare : la plupart des clients incluent */* en dernier recours.
Les facteurs de qualité : q=
Chaque type dans Accept peut avoir un poids entre 0 et 1 :
Accept: text/html;q=1.0, application/json;q=0.9, text/xml;q=0.5, */*;q=0.1
Plus le q est eleve, plus le format est préféré. Sans q explicite, la valeur par défaut est 1.0.
Le serveur trie par ordre de préférence et choisit le premier qu'il peut produire. Si le serveur sait faire du HTML et du JSON, il choisit HTML (q=1.0 vs q=0.9).
Un q=0 signifie "je ne veux surtout pas ce format". Utile pour exclure explicitement un type :
Accept: text/*, text/plain;q=0
Ca dit "je veux du texte, mais pas du texte brut". Subtil, mais parfois nécessaire.
Accept-Language : la negociation de langue
Meme mecanique pour la langue :
Accept-Language: fr-FR, fr;q=0.9, en;q=0.5
Ce client préféré le français de France, accepte le français générique, et l'anglais en dernier recours.
La réponse indique la langue choisie :
Content-Language: fr
En réalité, peu de serveurs font de la vraie negociation de langue cote serveur. La plupart des sites multilingues utilisent des URLs différentes (/fr/page, /en/page) ou des sous-domaines (fr.example.com). La negociation via Accept-Language est plus courante pour les API.
J'ai vu un cas amusant ou une API renvoyait les messages d'erreur dans la langue du header Accept-Language. Un développeur français debuggait un problème et recevait les erreurs en français, tandis que la documentation de l'API montrait des exemples en anglais. Il cherchait les mauvais messages d'erreur dans les logs. Trente minutes de perdu pour un header.
Accept-Encoding : la compression revisitee
On l'a vu dans l'article sur la compression, mais Accept-Encoding fait partie de la negociation de contenu :
Accept-Encoding: br, gzip, deflate
La réponse utilise Content-Encoding pour indiquer l'algorithme choisi. C'est le meme mecanisme de negociation, applique a l'encodage plutot qu'au format.
Le header Vary : protéger le cache
Voici le scénario qui casse tout. Un CDN cache ta réponse. Le premier client demande du JSON, le CDN stocke du JSON. Le deuxieme client demande du HTML, le CDN lui sert du JSON cache.
Le header Vary resout ce problème :
Vary: Accept, Accept-Language, Accept-Encoding
Ce header dit aux caches : "la réponse varie selon ces headers de requête. Cree une entree de cache séparée pour chaque combinaison."
Sans Vary, un proxy cache peut servir la mauvaise representation. C'est un bug silencieux et difficile a diagnostiquer parce qu'il depend de l'ordre d'arrivee des requêtes.
Attention a ne pas lister trop de headers dans Vary. Chaque header supplementaire multiplie le nombre d'entrees de cache possibles. Vary: Accept, Accept-Language, Accept-Encoding avec 3 formats, 5 langues et 3 encodages donne 45 variantes pour une seule URL. Le cache devient moins efficace.
Sur paltemps.fr, les API utilisent Vary: Accept parce qu'elles servent du JSON et du HTML selon le client. Le contenu statique utilise Vary: Accept-Encoding uniquement.
Negociation pilotee par le serveur vs par le client
Ce qu'on a vu jusqu'ici, c'est la negociation pilotee par le serveur (proactive). Le client envoie ses préférences, le serveur décidé.
L'alternative est la negociation pilotee par le client (reactive). Le serveur répond avec un 300 Multiple Choices et une liste de representations disponibles. Le client choisit ensuite celle qu'il veut.
HTTP/1.1 300 Multiple Choices
Link: </api/users/42.json>; rel="alternate"; type="application/json"
Link: </api/users/42.xml>; rel="alternate"; type="text/xml"
En theorie, c'est plus propre. En pratique, personne ne l'utilise. Le double aller-retour est trop coûteux, et les clients ne savent généralement pas gerer un 300. La negociation cote serveur domine.
Content-Type en réponse : le contrat final
Le header Content-Type dans la réponse est le résultat de toute cette negociation. Il dit au client exactement ce qu'il recoit :
Content-Type: application/json; charset=utf-8
Le charset=utf-8 est important pour les types textuels. Sans lui, le navigateur peut deviner l'encodage et se tromper. Toujours explicite, jamais d'ambiguite.
Pour les API modernes, application/json est devenu le standard de fait. Mais le header Accept permet a la meme URL de servir du HTML pour un navigateur et du JSON pour un client API. C'est elegant, et ca respecte le principe REST d'une URL unique par ressource.
Les types MIME les plus courants
| Type MIME | Usage |
|---|---|
text/html |
Pages web |
application/json |
API REST |
text/css |
Feuilles de style |
application/javascript |
Scripts |
image/webp |
Images modernes |
application/pdf |
Documents PDF |
multipart/form-data |
Upload de fichiers |
application/octet-stream |
Binaire générique |
Résumé
Acceptnegocie le type MIME,Accept-Languagela langue,Accept-Encodingla compression- Les facteurs de qualité
q=expriment les préférences du client de 0 a 1 Varyest obligatoire pour que les caches distinguent les variantes- La negociation cote serveur (proactive) est la norme en pratique
Content-Typedans la réponse est le résultat final de la negociation- Trop de headers dans
Varyfragmente le cache et réduit son efficacite
Article précédent : 10 - La compression
Article suivant : 12 - L'authentification HTTP