Come utilizzare correttamente i link POST
Nelle applicazioni web, specialmente nelle interfacce amministrative, dovrebbe essere una regola fondamentale che le azioni che modificano lo stato del server non vengano eseguite tramite il metodo HTTP GET. Come suggerisce il nome del metodo, GET dovrebbe servire solo per ottenere dati, non per modificarli. Per azioni come l'eliminazione di record, è preferibile utilizzare il metodo POST. Anche se l'ideale sarebbe il metodo DELETE, ma non può essere invocato senza JavaScript, quindi storicamente si usa POST.
Come farlo in pratica? Utilizzate questo semplice trucco. All'inizio del template, create un modulo ausiliario con
l'identificatore postForm
, che utilizzerete successivamente per i pulsanti di eliminazione:
<form method="post" id="postForm"></form>
Grazie a questo modulo, potete utilizzare un pulsante <button>
invece di un classico link
<a>
, che può essere stilizzato visivamente per assomigliare a un normale link. Ad esempio, il framework CSS
Bootstrap offre le classi btn btn-link
con cui potete ottenere che il pulsante non sia visivamente diverso dagli
altri link. Tramite l'attributo form="postForm"
lo colleghiamo al modulo pre-preparato:
<table>
<tr n:foreach="$posts as $post">
<td>{$post->title}</td>
<td>
<button class="btn btn-link" form="postForm" formaction="{link delete $post->id}">delete</button>
<!-- instead of <a n:href="delete $post->id">delete</a> -->
</td>
</tr>
</table>
Cliccando sul link, ora verrà invocata l'azione delete
. Per garantire che le richieste vengano accettate solo
tramite il metodo POST e dallo stesso dominio (che è una difesa efficace contro gli attacchi CSRF), utilizzate l'attributo
#[Requires]
:
use Nette\Application\Attributes\Requires;
class AdminPresenter extends Nette\Application\UI\Presenter
{
#[Requires(methods: 'POST', sameOrigin: true)]
public function actionDelete(int $id): void
{
$this->facade->deletePost($id); // codice ipotetico che elimina il record
$this->redirect('default');
}
}
L'attributo esiste da Nette Application 3.2 e potete saperne di più sulle sue possibilità nella pagina Come utilizzare l'attributo #Requires.
Se invece dell'azione actionDelete()
utilizzaste il segnale handleDelete()
, non è necessario
specificare sameOrigin: true
, perché i segnali hanno questa protezione impostata implicitamente:
#[Requires(methods: 'POST')]
public function handleDelete(int $id): void
{
$this->facade->deletePost($id);
$this->redirect('this');
}
Questo approccio non solo migliora la sicurezza della vostra applicazione, ma contribuisce anche al rispetto degli standard e delle pratiche web corrette. Utilizzando i metodi POST per le azioni che modificano lo stato, otterrete un'applicazione più robusta e sicura.