Как правильно использовать POST-ссылки

В веб-приложениях, особенно в административных интерфейсах, основным правилом должно быть то, что действия, изменяющие состояние на сервере, не выполняются HTTP-методом GET. Как следует из названия, GET должен служить только для получения данных, а не для их изменения. Для действий вроде удаления записей уместнее метод POST. Идеальным был бы метод DELETE, но вызвать его без JavaScript нельзя, поэтому исторически для таких действий используется POST.

Как реализовать это на практике? Воспользуйтесь простым приёмом. В начале шаблона макета создайте вспомогательную форму с идентификатором postForm. Затем вы будете использовать её для действий вроде кнопок удаления:

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

Благодаря этой форме вместо обычной ссылки <a> вы можете использовать <button>. Эту кнопку можно оформить так, чтобы она выглядела как обычная ссылка. Например, CSS-фреймворк Bootstrap предлагает классы btn btn-link, благодаря которым кнопка визуально не отличается от других ссылок. Атрибутом form="postForm" привяжите кнопку к подготовленной вспомогательной форме:

<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>
			<!-- вместо <a n:href="delete $post->id">delete</a> -->
		</td>
	</tr>
</table>

Нажатие на эту кнопку теперь вызывает действие delete. Чтобы запросы принимались только методом POST и только с того же домена (действенная защита от CSRF-атак), используйте атрибут #[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); // гипотетический код удаления записи
		$this->redirect('default');
	}
}

Этот атрибут доступен начиная с Nette Application 3.2. Подробнее о его возможностях можно узнать на странице Как использовать атрибут #Requires.

Если вместо действия actionDelete() вы использовали сигнал handleDelete(), указывать sameOrigin: true не нужно, потому что у сигналов эта защита включена по умолчанию:

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

Такой подход не только повышает безопасность вашего приложения, но и способствует соблюдению правильных веб-стандартов и практик. Использование методов POST для действий, меняющих состояние, делает приложение надёжнее и безопаснее.