Kako pravilno uporabljati POST povezave

V spletnih aplikacijah, zlasti v administrativnih vmesnikih, bi moralo biti osnovno pravilo, da se akcije, ki spreminjajo stanje strežnika, ne izvajajo prek metode HTTP GET. Kot pove že ime metode, bi moral GET služiti samo za pridobivanje podatkov, ne pa za njihovo spreminjanje. Za akcije, kot je na primer brisanje zapisov, je primernejša uporaba metode POST. Čeprav bi bila idealna metoda DELETE, je te brez JavaScripta ni mogoče izvesti, zato se zgodovinsko uporablja POST.

Kako to storiti v praksi? Uporabite ta preprost trik. Na začetku predloge si ustvarite pomožni obrazec z identifikatorjem postForm, ki ga nato uporabite za gumbe za brisanje:

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

Zahvaljujoč temu obrazcu lahko namesto klasične povezave <a> uporabite gumb <button>, ki ga lahko vizualno prilagodite tako, da izgleda kot običajna povezava. Na primer, CSS ogrodje Bootstrap ponuja razreda btn btn-link, s katerima dosežete, da gumb ne bo vizualno drugačen od ostalih povezav. Z atributom form="postForm" ga povežemo z vnaprej pripravljenim obrazcem:

<table>
	<tr n:foreach="$posts as $post">
		<td>{$post->title}</td>
		<td>
			<button class="btn btn-link" form="postForm" formaction="{link delete $post->id}">izbriši</button>
			<!-- namesto <a n:href="delete $post->id">izbriši</a> -->
		</td>
	</tr>
</table>

Ob kliku na povezavo se zdaj izvede akcija delete. Za zagotovitev, da bodo zahteve sprejete samo prek metode POST in iz iste domene (kar je učinkovita obramba pred napadi CSRF), uporabite atribut #[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); // hipotetična koda, ki briše zapis
		$this->redirect('default');
	}
}

Atribut obstaja od Nette Application 3.2 in več o njegovih možnostih boste izvedeli na strani Kako uporabljati atribut #Requires.

Če bi namesto akcije actionDelete() uporabljali signal handleDelete(), ni treba navajati sameOrigin: true, ker imajo signali to zaščito nastavljeno implicitno:

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

Ta pristop ne samo izboljšuje varnost vaše aplikacije, ampak tudi prispeva k spoštovanju pravilnih spletnih standardov in praks. Z uporabo metod POST za akcije, ki spreminjajo stanje, dosežete bolj robustno in varnejšo aplikacijo.