Un fichier `+page.server.js` peut exporter des _actions_, ce qui vous permet d'envoyer des données avec `POST` vers le serveur en utilisant un élément de formulaire `
``` Lorsque le bouton est cliqué, le navigateur va envoyer au serveur les données du formulaire via une requête `POST`, en exécutant l'action par défaut. > [!NOTE] Les actions utilisent toujours des requêtes `POST`, puisque les requêtes `GET` ne sont pas > censées avoir d'effets de bord. Nous pouvons aussi invoquer l'action depuis d'autres pages (par exemple, s'il y a un widget de connexion dans la barre de navigation du layout racine) en ajoutant l'attribut `action` pointant vers la page : ```html /// file: src/routes/+layout.svelte ``` ## Actions nommées [!VO]Named actions Plutôt qu'une seule action `default` par défaut, une page peut avoir autant d'actions nommées que nécessaire : ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { --- default: async (event) => {--- +++ login: async (event) => {+++ // TODO connecter l'utilisateur }, +++ register: async (event) => { // TODO inscrire l'utilisateur }+++ }; ``` Pour invoquer une action nommée, ajoutez un paramètre de requête comportant le nom de l'action préfixé par un caractère `/` : ```svelte ``` > [!NOTE] Il n'est pas possible d'avoir des actions par défaut dans le même fichier que des actions > nommées, car si vous ciblez une action nommée avec POST sans redirection, le paramètre de requête > est persisté dans l'URL, ce qui signifie que la prochaine requête POST par défaut va cibler > l'action nommée précédente. ## Anatomie d'une action [!VO]Anatomy of an action Chaque action reçoit un objet `RequestEvent`, vous permettant de lire les données avec `request.formData()`. Après le traitement de la requête (par exemple, pour connecter l'utilisateur ou l'utilisatrice en définissant un cookie), l'action peut répondre avec des données qui seront disponibles via la propriété `form` sur la page correspondante, et via `page.form` dans toute l'application, jusqu'à sa prochaine mise à jour. ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- import * as db from '$lib/server/db'; /** @type {import('./$types').PageServerLoad} */ export async function load({ cookies }) { const user = await db.getUserFromSession(cookies.get('sessionid')); return { user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO inscrire l'utilisateur } }; ``` ```svelte {#if form?.success}Connexion réussie ! Content de vous revoir, {data.user.name}
{/if} ``` > [!LEGACY] > `PageProps` a été ajouté dans la version 2.16.0 de SvelteKit. Dans les versions antérieures, vous > deviez typer les propriétés `data` et `form` individuellement : > ```js > /// file: +page.svelte > /** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */ > let { data, form } = $props(); > ``` > > Avec Svelte 4, vous utiliseriez plutôt `export let data` et `export let form` pour déclarer les > propriétés. ### Erreurs de validation [!VO]Validation errors Si la requête n'a pas pu être traitée à cause de données non valides, vous pouvez renvoyer des erreurs de validation — en plus des valeurs précédemment soumises par le formulaire — à l'utilisateur ou l'utilisatrice afin qu'il ou elle ré-essaye. La fonction `fail` vous permet de renvoyer un statut HTTP (généralement 400 ou 422, dans le cas d'erreurs de validation) avec les données. Le statut est rendu disponible via `page.status` et les données via `form` : ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- +++import { fail } from '@sveltejs/kit';+++ import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); +++ if (!email) { return fail(400, { email, missing: true }); }+++ const user = await db.getUser(email); +++ if (!user || user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); }+++ cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO inscrire l'utilisateur } }; ``` > [!NOTE] Notez que par précaution, nous renvoyons uniquement l'email à la page — pas le mot de > passe. ```svelte /// file: src/routes/login/+page.svelte ``` Les données renvoyées doivent être sérialisables en JSON. À part ça, la structure est complètement libre. Par exemple, si vous avez plusieurs formulaires sur la même page, vous pourriez différencier à quel élément ` ``` Notez que vous avez besoin de `deserialize` la réponse avant de la traiter en utilisant la méthode correspondante importée depuis `$app/forms`. `JSON.parse()` ne suffit pas car les actions de formulaires — comme les fonctions `load` — supportent également le renvoi d'objets `Date` ou `BigInt`. Si vous avez un fichier `+server.js` associé à votre `+page.server.js`, les requêtes `fetch` vont y être dirigées par défaut. Si vous souhaitez envoyer une requête `POST` à une action de `+page.server.js`, utilisez l'en-tête personnalisée `x-sveltekit-action` : ```js const response = await fetch(this.action, { method: 'POST', body: data, +++ headers: { 'x-sveltekit-action': 'true' }+++ }); ``` ## Alternatives Les actions de formulaire sont la méthode recommandée pour envoyer des données au serveur, puisqu'elles peuvent être améliorées progressivement, mais vous pouvez également utiliser des fichiers [`+server.js`](routing#server) pour exposer (par exemple) une API JSON. Voici ce à quoi une telle interaction pourrait ressembler : ```svelte ``` ```js // @errors: 2355 1360 2322 /// file: src/routes/api/ci/+server.js /** @type {import('./$types').RequestHandler} */ export function POST() { // faire quelque chose } ``` ## GET vs POST Comme nous venons de le voir, pour invoquer une action de formulaire, vous devez utiliser `method="POST"`. Certains formulaires n'ont pas besoin d'utiliser `POST` pour envoyer des données au serveur — les inputs de recherche par exemple. Pour ces cas-là, vous pouvez utiliser `method="GET"` (ou de manière équivalente, aucune `method`), et SvelteKit va traiter ces requêtes comme si elles venaient d'éléments ``, utilisant le routeur client plutôt qu'une navigation impliquant le rechargement complet de la page : ```html ``` Soumettre ce formulaire va déclencher une navigation vers `/search?q=...` et invoquer votre fonction `load`, mais ne va invoquer aucune action. Comme pour les éléments ``, vous pouvez définir les attributs [`data-sveltekit-reload`](link-options#data-sveltekit-reload), [`data-sveltekit-replacestate`](link-options#data-sveltekit-replacestate), [`data-sveltekit-keepfocus`](link-options#data-sveltekit-keepfocus) et [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) sur l'élément `