W komunikacji HTTP występują różne czasowniki. Najbardziej popularne to GET i POST. Dzisiaj dowiemy się jak odczytywać parametry tych zapytań w naszej akcji.
To trzeci post z serii “.NET Web”, w której nauczymy się budować aplikacje webowe. Równolegle będziemy pracować w dwóch różnych językach, w dwóch różnych frameworkach. Jeśli interesuje was tylko jeden, kliknijcie link poniżej by od razu przeskoczyć do odpowiedniego rozdziału.
Najpierw zapoznaj się z poprzednim postem, gdzie nauczyliśmy się filtrować zapytania w zależności od ścieżki i czasownika HTTP.
Ogólna teoria
Zanim zobaczymy jak uzyskać te parametry zapytań, powiemy sobie szybko o tym czym te zapytania się różnią.
Przede wszystkim, twórcy standardu HTTP założyli pewną logikę. Zapytanie z czasownikiem GET ma za zadanie odczytać pewną informację i zwrócić ją użytkownikowi. To jest podstawowa czynność jaką się wykonuje i to właśnie za jej pomocą otwierasz strony w przeglądarce. Zapytania typu POST mają za to w założeniu modyfikację stanu strony internetowej przez dodanie pewnej informacji. Są też inne czasowniki takie jak PUT (aktualizacja informacji) i DELETE (usunięcie informacji).
Często jednak nie używa się tych czasowników ściśle z ich przeznaczeniem. Jednak pewna reguła pozostaje: jeśli chcemy coś odczytać bez wprowadzania zmian, to powinniśmy użyć czasownika GET, a w przeciwnym wypadku czasownika POST.
Przeglądarki internetowe domyślnie wspierają tylko te dwa czasowniki. Odwiedzając stronę zawsze wywołamy GET. Żeby wywołać POST możemy użyć formy w HTMLu:
<form method="POST">
Parametry zapytań
Jak wyglądają parametry tych zapytań? W przypadku zapytania GET jest prosty schemat. Jeśli czytając ścieżkę zapytania napotkamy znak ?
to od niego zaczyna się lista parametrów w postaci key=value
oddzielonych od siebie znakiem ampersand &
. Np:
GET /search.php?name=Bob&age=24
Jak widać te parametry są częścią URI, która wyświetla nam się na pasku przeglądarki. Nie możemy więc przesyłać tą drogą hasła. Wtedy użyjemy zapytania POST, który swoje parametry ma w ciele zapytania HTTP, niewidocznym dla zwykłego użytkownika.
Tu już jest trochę ciężej, bo te parametry mogą mieć różną formę
Content-Type: application/x-www-form-urlencoded
name=Bob&age=24
Content-Type: multipart/form-data; boundary=--------43365972754266
--------43365972754266
Content-Disposition: form-data; name="name"
Bob
--------43365972754266
Content-Disposition: form-data; name="age"
24
--------43365972754266--
Content-Type: application/json
{ "name" : "Bob", "age" : 24 }
W zależności od tego jaki będzie typ zawartości naszego zapytania, parametry przyjmą różną formę. Przeglądarka przez wysłanie formy może wysłać albo application/x-www-form-urlencoded
albo multipart/form-data
. Natomiast kiedy sami piszemy kod tworzący takie zapytanie przy użyciu JavaScriptu to łatwiej będzie nam posłuzyć się notacją JSON.
Skoro już wiemy jak to mniej więcej wygląda to czas zabrać się do kodu.
F# z Suave
W Suave zarówno do przechowywania parametrów GET jak i POST został użyty słownik (w postaci listy par). Nasz obiekt HttpRequest
ma w sobie metody do uzyskania tych parametrów. Zwracają one Choice
, czyli dwie możliwości: Choice1Of2
od wartości pod tym kluczem, albo Choice2Of2
od komunikatu o błędzie.
Aby dostać parametr GET użyjemy metody queryParam
na obiekcie HttpRequest
, który uzyskamy korzystając z funkcji request
, która przyjmuje funkcję HttpRequest->WebPart
i zwraca WebPart
.
let action =
GET >=> request (fun req ->
let name = req.queryParam "name"
match name with
| Choice1Of2 name_ -> OK (sprintf "Hello %s" name_)
| Choice2Of2 _ -> OK "Provide a name"
)
W przypadku zapytania POST możemy stworzyć sobie pomocniczą funkcję
let getPostParam request key =
match request.header "content-type" with
| "application/x-www-form-urlencoded" -> request.formData key
| "multipart/form-data" | _ -> request.fieldData key
Dostaniemy z niej Choice
i możemy dalej go użyć jak wyżej.
W przypadku JSONa musielibyśmy utworzyć typ opisujący nasze argumenty, a następnie możemy użyć modułu Suave.Json
type MyJsonType = { name : string; age : int }
let getMyJsonValue reuqest =
Json.fromJson<MyJsonType> request.rawForm
Mi raczej niewygodnie korzysta się z typu Choice
więc moje funkcje wyciągające parametry używają takiej funkcji
let optionOfChoice c =
match c with
| Choice1Of2 x -> Some x
| _ -> None
I mogę wtedy korzystać z takich funkcji jak Option.map
lub Option.get
, aby dalej manipulować tą wartością.
Jeśli potrzebujemy jakiegoś parametru obowiązkowo, to kiedy go nie dotaniemy możemy zwrócić WebPart
o wartości never
. Wtedy wartością zwróconą (HttpContext option
) będzie None
.
let mustHaveIdParamFilter =
request (fun req ->
match req.queryParam "id" with
| Choice1Of2 _ -> fun ctx -> async { return (Some ctx) }
| _ -> never
)
Co dalej?
Spróbuj swoich sił wykonując zadanie albo przeczytaj następny artykuł: kiedyś.
C# z ASP.NET MVC
Podobnie jak tydzień temu odczytywaliśmy parametry URL przez argumenty naszej metody, tak samo będzie z parametrami GET i POST.
W przypadku paramterów GET będziemy mieli doczynienia z typami prostymi: int
, string
, bool
i jeśli jakiś parametr będzie opcjonalny to użyjemy typu, który może być nullem.
public IActionResult MyAction(string name, int age, int? type)
{
...
}
I tyle. Nie musimy robić nic więcej, bo silnik ASP.NET sam się domyśli jak te parametry nam odpowienio wstrzyknąć.
W przypadku parametrów POST, czyli tych znajdujących się w ciele zapytania, musimy dodać odpowiedni atrybut do argumentu
private class MyParams
{
public string Name { get; set; }
public int Age { get; set; }
}
public IActionResult MyAction([FromBody] MyParams params)
{
...
}
Nie musimy się martwić o typ parametrów (json/form-data/urlencoded), bo ASP.NET podejmuje odpowiednią decyzję za nas.
Jest również atrybut [FromForm]
, który pozwala na wyciągnięcie konkretnej wartości z formularza
public IActionResult MyAction([FromForm] string name, [FromForm] int age)
{
...
}
Co dalej?
Zachęcam do dalszej lektury o tym jak działa wiązanie parametrów w ASP.NET Core
Spróbuj swoich sił wykonując zadanie albo przeczytaj następny artykuł: kiedyś.
Zadanie
Utwórz prostą aplikację webową, która będzie przechowywać w pliku bazę danych w postaci
pokój|mebel
pokój|mebel
...
Modyfikacja i odczyt pliku będzie odbywać się przez aplikację webową. Będziemy chcieli mieć następujące akcje
GET /room
- zwraca listę pokojiGET /room/{pokój}
- zwraca listę mebli w pokojuGET /room?num=n
- zwraca n-ty z kolei pokójGET /room/{pokój}?num=n
- zwraca n-ty z kolei mebel w pokojuPOST /room
z danyminame=pokój
- tworzy nowy pokójPOST /room/{pokój}
z danyminame=mebel
- tworzy nowy mebel w pokoju
Do testowania programu możesz użyć aplikacji Postman.