Як правильно використовувати POST-посилання

У веб-додатках, особливо в адміністративних інтерфейсах, основним правилом має бути те, що дії, які змінюють стан сервера, не повинні виконуватися за допомогою методу HTTP GET. Як випливає з назви методу, GET слід використовувати тільки для отримання даних, а не для їх зміни. Для таких дій, як видалення записів, доцільніше використовувати метод POST. Хоча ідеальним варіантом було б використання методу DELETE, але його неможливо викликати без JavaScript, тому історично використовується POST.

Як це зробити на практиці? Використовуйте цей простий трюк. На початку вашого шаблону створіть допоміжну форму з ідентифікатором postForm, яку ви потім будете використовувати для кнопок видалення:

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

У цій формі ви можете використовувати <button> замість класичного <a> посилання, яке можна візуально модифікувати, щоб воно виглядало як звичайне. Наприклад, 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>
			<!-- instead of <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); // hypothetical code for deleting a record
		$this->redirect('default');
	}
}

Атрибут доступний з версії Nette Application 3.2, і ви можете дізнатися більше про його можливості на сторінці Як використовувати атрибут #Requires.

Якщо ви використовували сигнал handleDelete() замість дії actionDelete(), то не обов'язково вказувати sameOrigin: true, оскільки в сигналах цей захист встановлено неявно:

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

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