Как правилно да използвате 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');
	}
}

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

Ако сте използвали сигнала handleDelete() вместо действието actionDelete(), не е необходимо да посочвате sameOrigin: true, тъй като сигналите имат имплицитно зададена тази защита:

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

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