Română

Ghid complet pentru Acțiunile de Server în Next.js 14: bune practici pentru formulare, validare, securitate și tehnici avansate pentru aplicații web moderne.

Acțiunile de Server în Next.js 14: Stăpânirea Bunelor Practici pentru Gestionarea Formularelor

Next.js 14 introduce funcționalități puternice pentru crearea de aplicații web performante și prietenoase cu utilizatorul. Printre acestea, Acțiunile de Server (Server Actions) se remarcă drept o modalitate transformatoare de a gestiona trimiterile de formulare și mutațiile de date direct pe server. Acest ghid oferă o imagine de ansamblu completă a Acțiunilor de Server în Next.js 14, concentrându-se pe cele mai bune practici pentru gestionarea formularelor, validarea datelor, securitate și tehnici avansate. Vom explora exemple practice și vom oferi perspective acționabile pentru a vă ajuta să construiți aplicații web robuste și scalabile.

Ce sunt Acțiunile de Server din Next.js?

Acțiunile de Server sunt funcții asincrone care rulează pe server și pot fi invocate direct din componentele React. Acestea elimină necesitatea rutelor API tradiționale pentru gestionarea trimiterilor de formulare și a mutațiilor de date, rezultând un cod simplificat, securitate îmbunătățită și performanță sporită. Acțiunile de Server sunt Componente React de Server (RSCs), ceea ce înseamnă că sunt executate pe server, ducând la încărcări inițiale mai rapide ale paginilor și un SEO îmbunătățit.

Beneficiile Cheie ale Acțiunilor de Server:

Configurarea Proiectului Dvs. Next.js 14

Înainte de a aprofunda Acțiunile de Server, asigurați-vă că aveți un proiect Next.js 14 configurat. Dacă începeți de la zero, creați un nou proiect folosind următoarea comandă:

npx create-next-app@latest my-next-app

Asigurați-vă că proiectul dvs. utilizează structura de directoare app pentru a profita din plin de Componentele de Server și Acțiuni.

Gestionarea de Bază a Formularelor cu Acțiuni de Server

Să începem cu un exemplu simplu: un formular care trimite date pentru a crea un nou articol într-o bază de date. Vom folosi un formular simplu cu un câmp de introducere și un buton de trimitere.

Exemplu: Crearea unui Articol Nou

Mai întâi, definiți o funcție de Acțiune de Server în componenta dvs. React. Această funcție va gestiona logica de trimitere a formularului pe server.

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    await createItem(formData);
    setIsSubmitting(false);
  }

  return (
    
); }

Explicație:

Validarea Datelor

Validarea datelor este crucială pentru a asigura integritatea datelor și pentru a preveni vulnerabilitățile de securitate. Acțiunile de Server oferă o oportunitate excelentă de a efectua validarea pe server. Această abordare ajută la atenuarea riscurilor asociate doar cu validarea pe partea clientului.

Exemplu: Validarea Datelor de Intrare

Modificați Acțiunea de Server createItem pentru a include logica de validare.

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  if (!name || name.length < 3) {
    throw new Error('Item name must be at least 3 characters long.');
  }

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Explicație:

Utilizarea Bibliotecilor de Validare

Pentru scenarii de validare mai complexe, luați în considerare utilizarea bibliotecilor de validare precum:

Iată un exemplu folosind Zod:

// app/utils/validation.ts
import { z } from 'zod';

export const CreateItemSchema = z.object({
  name: z.string().min(3, 'Item name must be at least 3 characters long.'),
});
// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { CreateItemSchema } from '../utils/validation';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  const validatedFields = CreateItemSchema.safeParse({ name });

  if (!validatedFields.success) {
    return { errors: validatedFields.error.flatten().fieldErrors };
  }

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Explicație:

Considerații de Securitate

Acțiunile de Server îmbunătățesc securitatea prin executarea codului pe server, dar este totuși crucial să urmați cele mai bune practici de securitate pentru a vă proteja aplicația de amenințările comune.

Prevenirea Falsificării Cererilor între Site-uri (CSRF)

Atacurile CSRF exploatează încrederea pe care un site web o are în browserul unui utilizator. Pentru a preveni atacurile CSRF, implementați mecanisme de protecție CSRF.

Next.js gestionează automat protecția CSRF atunci când se utilizează Acțiuni de Server. Framework-ul generează și validează un token CSRF pentru fiecare trimitere de formular, asigurând că cererea provine din aplicația dvs.

Gestionarea Autentificării și Autorizării Utilizatorilor

Asigurați-vă că numai utilizatorii autorizați pot efectua anumite acțiuni. Implementați mecanisme de autentificare și autorizare pentru a proteja datele și funcționalitățile sensibile.

Iată un exemplu folosind NextAuth.js pentru a proteja o Acțiune de Server:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { getServerSession } from 'next-auth';
import { authOptions } from '../../app/api/auth/[...nextauth]/route';

async function createItem(formData: FormData) {
  'use server'

  const session = await getServerSession(authOptions);

  if (!session) {
    throw new Error('Unauthorized');
  }

  const name = formData.get('name') as string;

  // Simulate database interaction
  console.log('Creating item:', name, 'by user:', session.user?.email);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Explicație:

Curățarea Datelor de Intrare

Curățați datele de intrare pentru a preveni atacurile de tip Cross-Site Scripting (XSS). Atacurile XSS apar atunci când cod malițios este injectat într-un site web, compromițând potențial datele utilizatorului sau funcționalitatea aplicației.

Utilizați biblioteci precum DOMPurify sau sanitize-html pentru a curăța datele furnizate de utilizator înainte de a le procesa în Acțiunile dvs. de Server.

Tehnici Avansate

Acum că am acoperit elementele de bază, haideți să explorăm câteva tehnici avansate pentru utilizarea eficientă a Acțiunilor de Server.

Actualizări Optimiste

Actualizările optimiste oferă o experiență de utilizare mai bună prin actualizarea imediată a interfeței grafice ca și cum acțiunea va reuși, chiar înainte ca serverul să o confirme. Dacă acțiunea eșuează pe server, interfața grafică este readusă la starea anterioară.

// app/components/UpdateItemForm.tsx
'use client';

import { useState } from 'react';

async function updateItem(id: string, formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulate database interaction
  console.log('Updating item:', id, 'with name:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  // Simulate failure (for demonstration purposes)
  const shouldFail = Math.random() < 0.5;
  if (shouldFail) {
    throw new Error('Failed to update item.');
  }

  console.log('Item updated successfully!');
  return { name }; // Return the updated name
}

export default function UpdateItemForm({ id, initialName }: { id: string; initialName: string }) {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  const [itemName, setItemName] = useState(initialName);

  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);

    // Optimistically update the UI
    const newName = formData.get('name') as string;
    setItemName(newName);

    try {
      const result = await updateItem(id, formData);
      //If success then update is already reflected in UI through setItemName

    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
      // Revert the UI on error
      setItemName(initialName);
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    

Current Name: {itemName}

{errorMessage &&

{errorMessage}

}
); }

Explicație:

Revalidarea Datelor

După ce o Acțiune de Server modifică datele, este posibil să fie necesar să revalidați datele stocate în cache pentru a vă asigura că interfața grafică reflectă cele mai recente modificări. Next.js oferă mai multe moduri de a revalida datele:

Iată un exemplu de revalidare a unei căi după crearea unui articol nou:

// app/components/CreateItemForm.tsx
'use client';

import { useState } from 'react';
import { revalidatePath } from 'next/cache';

async function createItem(formData: FormData) {
  'use server'

  const name = formData.get('name') as string;

  // Simulate database interaction
  console.log('Creating item:', name);

  await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate latency

  console.log('Item created successfully!');

  revalidatePath('/items'); // Revalidate the /items path
}

export default function CreateItemForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);
  
  async function handleSubmit(formData: FormData) {
    setIsSubmitting(true);
    setErrorMessage(null);
    try {
      await createItem(formData);
    } catch (error: any) {
      setErrorMessage(error.message || 'An error occurred.');
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    
{errorMessage &&

{errorMessage}

}
); }

Explicație:

Cele Mai Bune Practici pentru Acțiunile de Server

Pentru a maximiza beneficiile Acțiunilor de Server, luați în considerare următoarele bune practici:

Capcane Comune și Cum să le Evitați

Deși Acțiunile de Server oferă numeroase avantaje, există câteva capcane comune de care trebuie să fiți conștienți:

Concluzie

Acțiunile de Server din Next.js 14 oferă o modalitate puternică și eficientă de a gestiona trimiterile de formulare și mutațiile de date direct pe server. Urmând cele mai bune practici prezentate în acest ghid, puteți construi aplicații web robuste, sigure și performante. Adoptați Acțiunile de Server pentru a vă simplifica codul, a spori securitatea și a îmbunătăți experiența generală a utilizatorului. Pe măsură ce integrați aceste principii, luați în considerare impactul global al alegerilor dvs. de dezvoltare. Asigurați-vă că formularele și procesele de gestionare a datelor sunt accesibile, sigure și prietenoase pentru diverse audiențe internaționale. Acest angajament față de incluziune nu numai că va îmbunătăți uzabilitatea aplicației dvs., dar îi va extinde și acoperirea și eficacitatea la scară globală.