# 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.

- Auteur : Clément Hadrot
- Publié le : 2023-03-02
- Mis à jour le : 2023-03-02
- Catégorie : Blocs Gutenberg
- URL : https://wpmoderne.dev.wordpress-developpement.fr/blocs/bloc-formulaire-front-rest-api-fetch-nonce/

## L’essentiel

- 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

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.
