Come usare correttamente i link POST
Nelle applicazioni web, in particolare nelle interfacce di amministrazione, dovrebbe valere una regola fondamentale: le azioni che modificano lo stato del server non si eseguono con il metodo HTTP GET. Come dice il nome, GET dovrebbe servire solo a ottenere dati, non a modificarli. Per azioni come l'eliminazione di record è più appropriato usare il metodo POST. Il metodo DELETE sarebbe ideale, ma non si può richiamare senza JavaScript, ed è per questo che storicamente si è usato POST per azioni del genere.
Come realizzarlo in pratica? Usate questo semplice trucco. All'inizio del vostro template di layout create un form di supporto
con l'ID postForm. Userete poi questo form per azioni come i pulsanti di eliminazione:
<form method="post" id="postForm"></form>
Grazie a questo form, al posto di un normale link <a> potete usare un <button>. Questo
pulsante si può stilizzare perché sembri un normale link. Il framework CSS Bootstrap, per esempio, offre le classi
btn btn-link, che rendono il pulsante visivamente indistinguibile dagli altri link. Con l'attributo
form="postForm" collegate il pulsante al form di supporto 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}">elimina</button>
<!-- invece di <a n:href="delete $post->id">elimina</a> -->
</td>
</tr>
</table>
Cliccando questo pulsante viene ora richiamata l'azione delete. Perché le richieste vengano accettate solo con il
metodo POST e provengano dallo stesso dominio (una difesa efficace contro gli attacchi CSRF), usate 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 per eliminare un record
$this->redirect('default');
}
}
Questo attributo è disponibile da Nette Application 3.2. Potete saperne di più sulle sue possibilità nella pagina Come usare l'attributo #Requires.
Se invece dell'azione actionDelete() usaste il segnale handleDelete(), non è necessario indicare
sameOrigin: true, perché i segnali hanno questa protezione attiva per impostazione predefinita:
#[Requires(methods: 'POST')]
public function handleDelete(int $id): void
{
$this->facade->deletePost($id);
$this->redirect('this');
}
Questo approccio non solo aumenta la sicurezza della vostra applicazione, ma favorisce anche il rispetto dei corretti standard e delle buone pratiche del web. Usare i metodi POST per le azioni che cambiano lo stato porta a un'applicazione più solida e più sicura.