Cómo utilizar correctamente los enlaces POST

En aplicaciones web, especialmente en interfaces administrativas, debería ser una regla básica que las acciones que cambian el estado del servidor no deberían realizarse a través del método HTTP GET. Como el nombre del método sugiere, GET debería usarse sólo para recuperar datos, no para cambiarlos. Para acciones como borrar registros, es más apropiado utilizar el método POST. Aunque lo ideal sería utilizar el método DELETE, éste no puede invocarse sin JavaScript, de ahí que históricamente se utilice POST.

¿Cómo hacerlo en la práctica? Utilice este sencillo truco. Al principio de su plantilla, cree un formulario de ayuda con el identificador postForm, que luego utilizará para los botones de eliminación:

<form method="post" id="postForm"></form>

Con este formulario, puedes utilizar un <button> en lugar del clásico enlace <a> que puede modificarse visualmente para que parezca un enlace normal. Por ejemplo, el framework CSS de Bootstrap ofrece las clases btn btn-link que permiten que el botón sea visualmente indistinguible de otros enlaces. Usando el atributo form="postForm", lo enlazamos al formulario ya 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 hacer clic en el enlace, ahora se invoca la acción delete. Para garantizar que las peticiones sólo se aceptan a través del método POST y desde el mismo dominio (lo que constituye una defensa eficaz contra los ataques CSRF), utilice el atributo #[Requires] atributo:

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); // hypothetical code for deleting a record
		$this->redirect('default');
	}
}

El atributo ha estado disponible desde Nette Application 3.2, y puede aprender más sobre sus capacidades en la página Cómo usar el atributo #Requires.

Si estuviera utilizando la señal handleDelete() en lugar de la acción actionDelete(), no es necesario especificar sameOrigin: true, porque las señales tienen esta protección establecida implícitamente:

#[Requires(methods: 'POST')]
public function handleDelete(int $id): void
{
	$this->facade->deletePost($id);
	$this->redirect('this');
}

Este enfoque no sólo mejora la seguridad de su aplicación, sino que también contribuye a adherirse a los estándares y prácticas web adecuados. Al utilizar métodos POST para acciones que cambian de estado, se consigue una aplicación más robusta y segura.