Como usar corretamente os links POST

Em aplicativos da Web, especialmente em interfaces administrativas, deve ser uma regra básica que as ações que alteram o estado do servidor não sejam executadas por meio do método HTTP GET. Como o nome do método sugere, o GET deve ser usado somente para recuperar dados, não para alterá-los. Para ações como a exclusão de registros, é mais apropriado usar o método POST. Embora o ideal fosse usar o método DELETE, ele não pode ser invocado sem JavaScript, por isso o POST é historicamente usado.

Como fazer isso na prática? Use este truque simples. No início do seu modelo, crie um formulário auxiliar com o identificador postForm, que você usará para os botões de exclusão:

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

Com esse formulário, você pode usar um <button> em vez do clássico link <a> clássico, que pode ser visualmente modificado para parecer um link normal. Por exemplo, a estrutura CSS do Bootstrap oferece as classes btn btn-link que permitem que o botão seja visualmente indistinguível de outros links. Usando o atributo form="postForm", nós o vinculamos ao formulário pré-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>

Ao clicar no link, a ação delete é invocada. Para garantir que as solicitações sejam aceitas somente por meio do método POST e do mesmo domínio (o que é uma defesa eficaz contra ataques CSRF), use o 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');
	}
}

O atributo está disponível desde o Nette Application 3.2, e você pode saber mais sobre seus recursos na página Como usar o atributo #Requires.

Se você estiver usando o sinal handleDelete() em vez da ação actionDelete(), não será necessário especificar sameOrigin: true, pois os sinais têm essa proteção definida implicitamente:

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

Essa abordagem não só aumenta a segurança do seu aplicativo, mas também contribui para a adesão aos padrões e práticas adequados da Web. Ao usar métodos POST para ações de mudança de estado, você obtém um aplicativo mais robusto e seguro.