Envoi de formulaire HTML7 min de lecture
Validation des formulaires HTML dans le navigateur et sur le serveur
Validez un formulaire avec required, type, pattern et min, stylisez les erreurs avec :user-invalid, et imposez les mêmes règles sur le serveur avec des définitions de champs.
Utilisez les contraintes intégrées au HTML, comme required, type="email", min, max et pattern, pour que le navigateur bloque les saisies incorrectes avant tout envoi. Imposez ensuite les mêmes règles sur le serveur, car les bots et les scripts contournent entièrement le navigateur. Avec Ikiform, définissez les champs du formulaire et chaque envoi est vérifié : un envoi incorrect reçoit 422 validation_failed avec un message par champ, que vous pouvez afficher avec setCustomValidity.
L'exemple est un formulaire de réservation d'atelier. Il contient le genre de champs où la validation se rend utile : une taille d'équipe avec des limites, une date qui ne peut pas être passée, un numéro de téléphone dans un seul format et un code de réservation de forme fixe.
Ce qui change ici
Il y a deux validateurs, et ils ne voient pas les mêmes requêtes. Le navigateur ne vérifie que les personnes qui utilisent votre page, avec ou sans scripts. Le serveur vérifie tout, y compris les bots qui envoient directement à l'endpoint et votre propre code qui appelle form.submit(), lequel ignore les vérifications du navigateur. Le travail consiste à faire dire la même chose aux deux.
- Une personne envoie le formulaire, et le navigateur vérifie les contraintes du HTML. Un champ invalide reçoit un message et rien n'est envoyé.
- Un envoi valide atteint Ikiform, qui le vérifie par rapport à la définition de champs du formulaire et stocke des valeurs typées.
- Un bot ou un script envoie directement à l'endpoint, donc aucune vérification du navigateur ne s'exécute.
- Ikiform le vérifie par rapport à la même définition et le refuse avec 422 avant que quoi que ce soit ne soit stocké.
Le niveau navigateur
Aperçu
Exécute le validateur d'Ikiform dans votre navigateur. Rien n'est envoyé.
ws-12.<form id="workshop" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<label for="name">Your name</label>
<input id="name" name="name" autocomplete="name" maxlength="120" required />
<label for="email">Work email</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="team_size">People attending</label>
<input id="team_size" name="team_size" type="number" min="2" max="20" step="1" required />
<label for="date">Preferred date</label>
<input id="date" name="date" type="date" min="2026-11-02" required />
<label for="phone">Phone</label>
<input id="phone" name="phone" type="tel" autocomplete="tel" placeholder="+14155550123"
pattern="\+[1-9][0-9]{6,14}" title="International format, like +14155550123" />
<label for="code">Booking code</label>
<input id="code" name="code" pattern="WS-[0-9]{4}" title="Codes look like WS-1234" />
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
<button>Request the workshop</button>
</form>Chaque attribut correspond à un état de validité, que vous pouvez lire depuis input.validity dans la console :
| Attribut | État de validité | Remarques |
|---|---|---|
required | valueMissing | Pour un groupe de boutons radio, un seul radio avec required couvre le groupe |
type="email", type="url" | typeMismatch | Permissif : a@b passe pour un e-mail dans les navigateurs |
pattern | patternMismatch | Doit correspondre à toute la valeur. Ignoré sur un champ facultatif vide |
minlength, maxlength | tooShort, tooLong | Vérifié seulement après que la personne a modifié la valeur |
min, max | rangeUnderflow, rangeOverflow | Nombres, dates et heures |
step | stepMismatch | step="1" refuse les décimales |
| Saisie non analysable | badInput | Des lettres tapées dans un champ numérique dans certains navigateurs |
setCustomValidity() | customError | Tout message que vous définissez, jusqu'à ce que vous le remettiez à vide |
Des patterns dans deux moteurs
Les navigateurs compilent pattern avec le flag v, plus strict que les expressions régulières que la plupart des gens écrivent : un - littéral dans une classe de caractères doit être échappé, comme dans [a-z\-]. Ikiform compile le pattern d'un champ avec le flag u et le fait aussi correspondre à toute la valeur. Écrivez des patterns valides dans les deux, comme le code de réservation ci-dessus, et testez chacun dans l'aperçu du formulaire avant de le publier.
Styliser les erreurs
:invalid correspond à un champ obligatoire vide dès le chargement de la page, donc tous les champs s'affichent en rouge avant que quiconque ne tape. :user-invalid ne correspond qu'après que la personne a interagi avec le champ ou tenté d'envoyer, ce qui est presque toujours ce que vous voulez :
<style>
#workshop :user-invalid {
border-color: #b42318;
}
</style>Le niveau serveur
Collez la définition ci-dessus dans l'onglet Fields du formulaire. Dès lors, Ikiform valide chaque envoi par rapport à elle, quelle qu'en soit la source, et stocke des valeurs converties : team_size comme nombre, phone sans séparateurs, date au format YYYY-MM-DD. Un envoi qui enfreint une règle est refusé, rien n'est stocké, et la réponse indique quel champ a échoué :
| Champ | Envoyé | Message |
|---|---|---|
team_size | 25 | Must be at most 20 |
team_size | 2.5 | Enter a whole number |
date | 2026-10-30 | Must be on or after 2026-11-02 |
phone | 415 555 0123 | Use international format, e.g. +14155550123 |
code | ws-12 | Codes look like WS-1234 |
website | N'importe quelle valeur | Unknown field "website" |
La dernière ligne compte. Avec des champs définis, Ikiform refuse tout champ absent de la définition, donc un input renommé échoue de façon visible au lieu d'être stocké sous le mauvais nom. Les champs commençant par _ et le honeypot ne comptent pas. Chaque type et chaque règle figurent dans Champs et validation.
Avec un envoi classique
Aucun code nécessaire. Un envoi classique refusé affiche la page d'erreur hébergée d'Ikiform avec le statut 422, chaque problème sous le libellé de votre champ, et un lien vers la page d'où vient l'envoi. Le bouton Back du navigateur conserve généralement ce que la personne a saisi ; le lien recharge le formulaire vide.
Afficher les erreurs du serveur sur les champs
Avec un script, injectez chaque message du serveur dans l'interface d'erreur propre au navigateur. setCustomValidity marque le champ comme invalide avec ce texte, et reportValidity l'affiche sous forme de bulle habituelle et place le focus sur le premier :
const form = document.querySelector("#workshop");
for (const control of form.elements) {
control.addEventListener("input", () => control.setCustomValidity?.(""));
}
form.addEventListener("submit", async (event) => {
event.preventDefault();
const response = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
const result = await response.json();
if (result.ok) {
form.replaceWith("Thanks. We'll confirm the date by email.");
return;
}
const fields = result.error.fields ?? {};
for (const [name, messages] of Object.entries(fields)) {
form.elements.namedItem(name)?.setCustomValidity?.(messages[0]);
}
if (!form.reportValidity()) return;
alert(result.error.message);
});- Effacez le message personnalisé sur
input. Un champ avec une erreur personnalisée reste invalide jusqu'à ce que vous la remettiez à une chaîne vide, et la personne ne peut alors plus du tout envoyer. - Les vérifications du navigateur s'exécutent toujours avant l'événement
submit, donc le serveur ne voit que les envois qui les ont passées. - Si l'erreur n'a pas de
fields, comme429 rate_limited,reportValidity()renvoietrueet le script se rabat sur le message général.
Vérifier les règles du serveur avant l'envoi
Pour repérer avant la requête les règles propres au serveur, comme une liste de domaines d'e-mail, chargez la définition publique du formulaire et exécutez le validate du SDK, qui applique dans le navigateur les mêmes règles et messages de champ :
import { getSchema, validate } from "@ikiform/sdk";
const form = document.querySelector("#workshop");
const result = await getSchema("frm_xxxxxxxxxxxxxxxx");
const fields = result.ok ? result.schema.fields : [];
form.addEventListener("submit", (event) => {
const errors = validate(fields, new FormData(form));
for (const control of form.elements) control.setCustomValidity?.(errors[control.name] ?? "");
if (!form.reportValidity()) event.preventDefault();
});getSchema n'expose que les définitions de champs, qui sont de toute façon publiques : n'importe qui peut les lire depuis /f/<formId>/schema.Vérifiez que tout fonctionne
- Dans Allowed domains du formulaire, ajoutez le domaine de votre site, plus
localhostpendant le développement. Laissez vide pour accepter les envois de partout. - Envoyez le formulaire vide. Le navigateur l'arrête à Votre nom et rien n'apparaît dans le panneau Réseau.
- Retirez
maxde la taille d'équipe dans les outils de développement, saisissez25et envoyez. L'envoi classique affiche la page d'erreur d'Ikiform ; l'envoi par script affiche Must be at most 20 sur le champ. - Envoyez avec curl, sans passer par le navigateur :
curl -H "Accept: application/json" -H "Origin: https://example.com" -d team_size=abc https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx, avec votre propre domaine autorisé comme origine. La réponse est un422avec un message pourteam_sizeet pour chaque champ obligatoire manquant. - Ouvrez Submissions dans le tableau de bord. La nouvelle soumission y est, avec tous les champs envoyés.
Pièges
| Problème | Cause et solution |
|---|---|
| Le formulaire ne s'envoie plus après une erreur du serveur | Un message de validité personnalisé n'a jamais été effacé. Réinitialisez-le avec setCustomValidity("") sur input |
minlength ne se déclenche pas sur les valeurs préremplies | tooShort ne s'applique qu'après que la personne a modifié le champ. La règle du serveur le rattrape |
Un pattern est entièrement ignoré | Il est invalide avec le flag v, et les navigateurs ignorent entièrement un pattern invalide. Échappez - et les autres caractères spéciaux dans les classes |
| Les erreurs s'affichent avant que quiconque ne tape | Le CSS utilise :invalid. Passez à :user-invalid |
422 validation_failed | L'envoi ne correspond pas à la définition des champs du formulaire. error.fields nomme chaque champ et la raison. Faites correspondre le name de l'input à l'id du champ. |
Le déroulement complet d'un envoi se trouve dans la vue d'ensemble, et Erreurs liste chaque code qu'un envoi peut recevoir.
Questions
Non. Elle ne s'exécute que dans le navigateur sur votre page, et les bots ou les scripts envoient directement à l'endpoint. Validez aussi sur le serveur. Avec Ikiform, définissez les champs du formulaire et chaque envoi est vérifié.
Appelez input.setCustomValidity(message) puis form.reportValidity(), qui affiche le message dans la bulle habituelle du navigateur. Effacez-le avec setCustomValidity("") quand la personne modifie le champ.
:invalid correspond à tout champ qui ne respecte pas ses contraintes, y compris les champs obligatoires vides au chargement de la page. :user-invalid ne correspond qu'après que la personne a interagi avec le champ ou tenté d'envoyer.
422 validation_failed, sans rien stocker. Les requêtes JSON reçoivent error.fields, qui associe chaque identifiant de champ à ses messages ; les envois de formulaire classiques reçoivent une page d'erreur hébergée qui les liste par libellé de champ.
Une étape vous bloque, ou vous voulez un guide pour votre cas ? Écrivez à hello@ikiform.com.