Un paramètre ?format=csv dans l’URL fonctionne parfaitement pour choisir un format de sortie alternatif, mais il mélange une information de transport — comment le client souhaite recevoir la donnée — avec les paramètres métier de la requête elle-même. L’en-tête HTTP Accept, prévu précisément pour cet usage depuis les débuts du protocole, permet de séparer les deux préoccupations sans rien ajouter à l’URL.
Ce que fait normalement Accept
Dans une architecture REST classique, un client envoie Accept: application/json ou Accept: text/csv pour indiquer au serveur le format de réponse qu’il préfère recevoir, laissant au serveur la responsabilité de choisir la représentation la plus adaptée parmi celles qu’il sait produire. L’API REST native de WordPress ne pratique pas cette négociation automatiquement : elle renvoie toujours du JSON, quel que soit l’en-tête Accept transmis. Pour un contrôleur personnalisé, rien n’empêche cependant d’implémenter cette logique soi-même.
Implémenter la négociation dans un contrôleur

register_rest_route( 'catalogue/v1', '/produits', array(
'methods' => 'GET',
'callback' => 'lister_produits_negocie',
) );
function lister_produits_negocie( WP_REST_Request $request ) {
$accept = $request->get_header( 'accept' );
$produits = get_produits_actifs();
if ( $accept && false !== strpos( $accept, 'text/csv' ) ) {
$lignes = array( 'id,nom,prix' );
foreach ( $produits as $produit ) {
$lignes[] = sprintf( '%d,%s,%s', $produit->id, $produit->nom, $produit->prix );
}
$reponse = new WP_REST_Response( implode( "\n", $lignes ) );
$reponse->header( 'Content-Type', 'text/csv; charset=utf-8' );
return $reponse;
}
return new WP_REST_Response( $produits );
}
La méthode get_header() de l’objet WP_REST_Request lit directement la valeur de l’en-tête transmis par le client, sans distinction de casse. La comparaison se fait ici de façon simple par recherche de sous-chaîne, ce qui suffit dans la majorité des cas concrets, même si une implémentation plus stricte pourrait analyser précisément la syntaxe complète de l’en-tête Accept, qui autorise plusieurs types pondérés séparés par des virgules.
Annoncer correctement la réponse
Le point souvent oublié dans ce genre d’implémentation tient à l’en-tête Content-Type de la réponse elle-même. Sans lui, un client qui a demandé du CSV recevrait un contenu au bon format mais annoncé comme du JSON, ce qui perturbe les bibliothèques HTTP qui décident de leur méthode de parsing en fonction de cet en-tête plutôt que du contenu réel reçu.
Limites de cette approche
- Chaque contrôleur doit implémenter sa propre logique de négociation ; rien n’est mutualisé nativement par WordPress
- Un en-tête
Acceptmal formé ou absent doit toujours retomber sur un format par défaut raisonnable, ici JSON - Les outils de mise en cache intermédiaires doivent être configurés pour varier leur clé de cache selon l’en-tête
Accept, via l’en-tête de réponseVary: Accept, sous peine de servir le mauvais format à un client suivant
Variante
Pour une route qui doit gérer plus de deux formats, structurer la négociation autour d’un tableau associant chaque type MIME à sa fonction de sérialisation dédiée reste plus maintenable qu’une cascade de conditions if répétées, surtout si de nouveaux formats sont amenés à s’ajouter par la suite.
En résumé
Négocier le format d’une réponse REST via l’en-tête Accept plutôt qu’un paramètre d’URL dédié garde l’URL propre et respecte l’esprit du protocole HTTP tel qu’il a été pensé. WordPress ne fournit aucun mécanisme automatique pour cela, mais l’implémenter manuellement dans un contrôleur personnalisé reste accessible, à condition de soigner à la fois la lecture de l’en-tête entrant et l’annonce correcte du Content-Type en sortie.