Cómo usar correctamente los enlaces POST
En las aplicaciones web, sobre todo en las interfaces de administración, debería regir una regla fundamental: las acciones que modifican el estado del servidor no se realizan con el método HTTP GET. Como su nombre indica, GET debería servir solo para obtener datos, no para modificarlos. Para acciones como borrar registros es más adecuado el método POST. El método ideal sería DELETE, pero no se puede invocar sin JavaScript, y por eso históricamente se ha usado POST para estas acciones.
¿Cómo llevarlo a la práctica? Use este sencillo truco. Al principio de la plantilla del layout, cree un formulario auxiliar
con el ID postForm. Después usará ese formulario para acciones como los botones de borrado:
<form method="post" id="postForm"></form>
Gracias a este formulario, en lugar de un enlace <a> corriente puede usar un <button>.
Ese botón se puede estilizar para que parezca un enlace normal. Por ejemplo, el framework CSS Bootstrap ofrece las clases
btn btn-link, que hacen que el botón sea visualmente indistinguible de los demás enlaces. Con el atributo
form="postForm" enlace el botón con el formulario auxiliar preparado:
<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>
Al pulsar este botón se invoca ahora la acción delete. Para asegurarse de que las peticiones solo se aceptan por
el método POST y proceden del mismo dominio (una defensa eficaz frente a los ataques CSRF), use el atributo
#[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); // código hipotético para borrar un registro
$this->redirect('default');
}
}
Este atributo está disponible desde Nette Application 3.2. Puede conocer mejor sus posibilidades en la página Cómo usar el atributo #Requires.
Si en lugar de la acción actionDelete() usara la señal handleDelete(), no hace falta indicar
sameOrigin: true, porque las señales tienen esta protección activada de forma predeterminada:
#[Requires(methods: 'POST')]
public function handleDelete(int $id): void
{
$this->facade->deletePost($id);
$this->redirect('this');
}
Este enfoque no solo refuerza la seguridad de su aplicación, sino que además fomenta el respeto a los estándares y las prácticas correctas de la web. Usar el método POST para las acciones que cambian el estado da como resultado una aplicación más robusta y segura.