vendredi 25 septembre 2026

À propos

Contact

Blocs Gutenberg

Un bloc formulaire front qui envoie ses données à une route REST

Construire un bloc de formulaire de contact avec validation côté client, envoi fetch vers une route REST personnalisée et messages d'erreur annoncés aux lecteurs d'écran.

Par Clément Hadrot • 2 mars 2023 • 5 min de lecture • Aucun commentaire
Un bloc formulaire front qui envoie ses données à une route REST

Un client vendait des rendez-vous de conseil et voulait un formulaire de prise de contact directement intégré comme bloc, réutilisable sur plusieurs pages, sans dépendre d’une extension tierce de formulaires. Le cahier des charges tenait en une phrase : « un champ nom, un champ e-mail, un message, et ça doit fonctionner sans rechargement de page ».

Ce cas d’usage est un bon prétexte pour construire, de bout en bout, un bloc front qui collecte des données, les valide, puis les envoie à une route REST personnalisée via fetch et l’API apiFetch de WordPress. Voici comment on l’a structuré, avec les pièges rencontrés en cours de route.

Déclarer la route REST côté serveur

La route s’enregistre classiquement avec register_rest_route() sur le hook rest_api_init. Le point à ne pas négliger : le callback de permission. Un formulaire de contact public doit rester accessible aux visiteurs non connectés, donc permission_callback renvoie true, mais cela ne dispense pas de vérifier le nonce transmis dans l’en-tête pour se protéger du CSRF, ni de valider et d’assainir chaque champ reçu.

add_action( 'rest_api_init', function () {
    register_rest_route(
        'agence-contact/v1',
        '/message',
        array(
            'methods'             => 'POST',
            'callback'            => 'agence_contact_traiter_message',
            'permission_callback' => '__return_true',
            'args'                => array(
                'nom'     => array( 'required' => true, 'type' => 'string' ),
                'email'   => array( 'required' => true, 'type' => 'string' ),
                'message' => array( 'required' => true, 'type' => 'string' ),
            ),
        )
    );
} );

function agence_contact_traiter_message( WP_REST_Request $request ) {
    $email = sanitize_email( $request->get_param( 'email' ) );
    if ( ! is_email( $email ) ) {
        return new WP_Error( 'email_invalide', 'Adresse e-mail invalide.', array( 'status' => 400 ) );
    }

    $nom     = sanitize_text_field( $request->get_param( 'nom' ) );
    $message = sanitize_textarea_field( $request->get_param( 'message' ) );

    wp_mail(
        get_option( 'admin_email' ),
        sprintf( 'Nouveau message de %s', $nom ),
        $message,
        array( 'Reply-To: ' . $email )
    );

    return new WP_REST_Response( array( 'statut' => 'envoye' ), 200 );
}

Le nonce, sans surcouche

Le point qui bloque le plus souvent les débutants : WordPress fournit déjà un nonce prêt à l’emploi pour l’API REST via wp_create_nonce( 'wp_rest' ), à condition de le rendre disponible côté front. Sur le front public (pas dans l’éditeur), on le sort proprement avec wp_localize_script() ou wp_add_inline_script() attaché au view.js du bloc, jamais en dur dans le HTML statique du save() car cela figerait un nonce périmé dans le cache de page.

function agence_contact_enregistrer_nonce() {
    wp_add_inline_script(
        'agence-contact-view-script',
        'window.agenceContactNonce = ' . wp_json_encode( wp_create_nonce( 'wp_rest' ) ) . ';',
        'before'
    );
}
add_action( 'wp_enqueue_scripts', 'agence_contact_enregistrer_nonce' );
L'essentiel à retenir : Route REST dédiée avec vérification du nonce wp_rest ; Validation front avant envoi, validation serveur obligatoire aussi ; Messages d'erreur liés au champ avec aria-live

Validation front avant l’envoi

La validation côté client sert l’expérience utilisateur, pas la sécurité : elle évite un aller-retour réseau inutile pour une erreur de saisie évidente, mais elle ne remplace jamais la validation serveur vue plus haut. Un visiteur peut toujours désactiver JavaScript ou appeler la route directement avec curl.

async function envoyerFormulaire( evenement ) {
    evenement.preventDefault();
    const form = evenement.target;
    const email = form.querySelector( '[name="email"]' ).value;

    if ( ! /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( email ) ) {
        afficherErreur( form, 'email', 'Vérifiez le format de votre adresse e-mail.' );
        return;
    }

    const reponse = await fetch( '/wp-json/agence-contact/v1/message', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-WP-Nonce': window.agenceContactNonce,
        },
        body: JSON.stringify( {
            nom: form.querySelector( '[name="nom"]' ).value,
            email,
            message: form.querySelector( '[name="message"]' ).value,
        } ),
    } );

    if ( reponse.ok ) {
        afficherConfirmation( form );
    } else {
        const erreur = await reponse.json();
        afficherErreur( form, 'general', erreur.message );
    }
}

Rendre les erreurs perceptibles

Un message d’erreur qui apparaît visuellement mais que personne n’annonce à un lecteur d’écran n’aide qu’une partie des visiteurs. La fonction afficherErreur() associe le message au champ via aria-describedby, et le conteneur global des erreurs porte aria-live="polite" pour que son apparition soit lue automatiquement, sans déplacer le focus de force.

  • Chaque champ en erreur reçoit aria-invalid="true" le temps que l’utilisateur corrige.
  • Le message d’erreur est un élément avec un id unique, référencé par aria-describedby sur le champ.
  • La confirmation d’envoi réussi est elle aussi annoncée via une zone aria-live, pas seulement un changement de couleur.

Sur ce projet, tester le formulaire au clavier seul, sans souris, a révélé que le focus restait sur le bouton « Envoyer » après une erreur — l’utilisateur ne savait pas où regarder. On a ajouté un déplacement de focus explicite vers le premier champ en erreur, ce qui a réglé le problème en une ligne de JavaScript.

En résumé

Un bloc de formulaire front autonome tient en trois briques solidaires : une route REST qui ne fait confiance à rien côté serveur, un nonce distribué proprement sans le figer dans le HTML mis en cache, et une couche d’accessibilité qui traite les erreurs comme des informations à annoncer, pas seulement à afficher. Le tout sans dépendance à une extension tierce, pour un formulaire de trois champs qui n’en avait pas besoin.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi