Jak poprawnie używać linków POST
W aplikacjach internetowych, zwłaszcza w interfejsach administracyjnych, podstawową zasadą powinno być, że akcje zmieniające stan serwera nie powinny być wykonywane za pomocą metody HTTP GET. Jak sama nazwa metody wskazuje, GET powinien służyć wyłącznie do pobierania danych, a nie do ich zmiany. Dla akcji takich jak na przykład usuwanie rekordów bardziej odpowiednie jest użycie metody POST. Chociaż idealna byłaby metoda DELETE, ale tej nie można wywołać bez JavaScriptu, dlatego historycznie używa się POST.
Jak to zrobić w praktyce? Wykorzystaj ten prosty trik. Na początku szablonu stworzysz pomocniczy formularz z identyfikatorem
postForm
, który następnie użyjesz do przycisków usuwania:
<form method="post" id="postForm"></form>
Dzięki temu formularzowi możesz zamiast klasycznego linku <a>
użyć przycisku
<button>
, który można wizualnie dostosować tak, aby wyglądał jak zwykły link. Na przykład framework CSS
Bootstrap oferuje klasy btn btn-link
, dzięki którym osiągniesz to, że przycisk nie będzie wizualnie różnił
się od innych linków. Za pomocą atrybutu form="postForm"
powiążemy go z przygotowanym formularzem:
<table>
<tr n:foreach="$posts as $post">
<td>{$post->title}</td>
<td>
<button class="btn btn-link" form="postForm" formaction="{link delete $post->id}">usuń</button>
<!-- zamiast <a n:href="delete $post->id">usuń</a> -->
</td>
</tr>
</table>
Po kliknięciu na link zostanie teraz wywołana akcja delete
. Aby zapewnić, że żądania będą przyjmowane
wyłącznie za pomocą metody POST i z tej samej domeny (co jest skuteczną obroną przed atakami CSRF), użyj atrybutu
#[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); // hipotetyczny kod usuwający rekord
$this->redirect('default');
}
}
Atrybut istnieje od Nette Application 3.2, a więcej o jego możliwościach dowiesz się na stronie Jak używać atrybutu #Requires.
Gdybyś zamiast akcji actionDelete()
używał sygnału handleDelete()
, nie jest konieczne podawanie
sameOrigin: true
, ponieważ sygnały mają tę ochronę ustawioną domyślnie:
#[Requires(methods: 'POST')]
public function handleDelete(int $id): void
{
$this->facade->deletePost($id);
$this->redirect('this');
}
Takie podejście nie tylko poprawia bezpieczeństwo Twojej aplikacji, ale także przyczynia się do przestrzegania prawidłowych standardów i praktyk internetowych. Wykorzystując metody POST do akcji zmieniających stan, osiągniesz bardziej solidną i bezpieczniejszą aplikację.