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.