Jak správně používat POST odkazy

Ve webových aplikacích, zejména v administrativních rozhraních, by mělo být základním pravidlem, že akce měnící stav serveru by neměly být prováděny prostřednictvím HTTP metody GET. Jak název metody napovídá, GET by měl sloužit pouze k získání dat, nikoli k jejich změně. Pro akce jako třeba mazání záznamů je vhodnější použít metodu POST. I když ideální by byla metoda DELETE, ale tu nelze bez JavaScriptu vyvolat, proto se historicky používá POST.

Jak na to v praxi? Využijte tento jednoduchý trik. Na začátku šablony si vytvoříte pomocný formulář s identifikátorem postForm, který následně použijete pro mazací tlačítka:

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

Díky tomuto formuláři můžete místo klasického odkazu <a> použít tlačítko <button>, které lze vizuálně upravit tak, aby vypadalo jako běžný odkaz. Například CSS framework Bootstrap nabízí třídy btn btn-link se kterými dosáhnete toho, že tlačítko nebude vizuálně odlišné od ostatních odkazů. Pomocí atributu form="postForm" ho provážeme s předpřipraveným formulářem:

<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>

Při kliknutí na odkaz se nyní vyvolá akce delete. Pro zajištění, že požadavky budou přijímány pouze prostřednictvím metody POST a z téže domény (což je účinná obrana proti CSRF útokům), použijte 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); // hypotetický kód mazající záznam
		$this->redirect('default');
	}
}

Atribut existuje od Nette Application 3.2 a více o jeho možnostech se dozvíte na stránce Jak používat atribut #Requires.

Pokud byste místo akce actionDelete() používali signál handleDelete(), není nutné uvádět sameOrigin: true, protože signály mají tuto ochranu nastavenou implicitně:

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

Tento přístup nejenže zlepšuje bezpečnost vaší aplikace, ale také přispívá k dodržování správných webových standardů a praxe. Využitím metod POST pro akce měnící stav dosáhnete robustnější a bezpečnější aplikace.