Najważniejsze zagadnienia, które musisz znać
5 godz. 53 min · JavaScript · Full-stack i Programowanie
Adam GospodarczykPrzesyłanie oraz transformacja danych to jeden z najważniejszych elementów działania każdej aplikacji. Dobre zrozumienie tego w jaki sposób odbywa się ten proces, daje równocześnie lepsze zrozumienie całej struktury oraz podejmowania decyzji projektowych. W tym kursie znajdziesz lekcje, które pozwolą Ci uporządkować wiedzę na temat protokołu HTTP oraz formatów wymiany danych.
REST oraz GraphQL to nadal nieodłączone zagadnienia tematu wymiany informacji w kontekście aplikacji webowych. Pomimo tego, że ich koncepcje zwykle są bardzo dobrze opisane w różnych źródłach, tak ich wykorzystanie "na produkcji" potrafi sprawić problemy. W szczególności gdy weźmiemy pod uwagę, wyjątki oraz fakt, że nie każda aplikacja będzie wymagać implementacji REST czy GraphQL w 100%. W tym kursie znajdziesz wiele praktycznych wskazówek związanych z pracą z REST API oraz wprowadzenie do pracy z GraphQL.
Niektóre błędy w aplikacji są bardzo pożądane, w szczególności gdy jasno informują użytkownika o tym, co się wydarzyło oraz o tym, co może z tym zrobić. Pomimo tego, że w wielu przypadkach opracowanie komunikatów leży w rękach osoby odpowiedzialnej za copywriting (i/lub ux writing), tak sama implementacja tych komunikatów odbywa się po stronie kodu. Nie rzadko okazuje się, że poprawna i skuteczna walidacja danych oraz obsługa błędów nie jest oczywista w "produkcyjnych" sytuacjach. W tym kursie znajdziesz najczęściej spotykane problemy oraz sposoby ich rozwiązania.
Bazy danych są praktycznie nieodłączną częścią każdej aplikacji. W roli full-stacka prędzej czy później przyjdzie Ci z nimi pracować a nie rzadko nawet projektować oraz rozbudowywać ich struktury. W tym kursie znajdziesz informacje o bazach danych, które pozwolą Ci je zrozumieć oraz od razu poznać użyteczne techniki, które pomogą Ci w pracy z nimi, zarówno na produkcji jak i w środowisku lokalnym czy testowym.
Nie wszystkie informacje w aplikacji są publiczne. Niektóre wprost nie mogą takie być. Z tego powodu niezbędne są sposoby na to aby poznać a potem weryfikować tożsamość użytkownika, by na jej podstawie przydzielać mu dostęp wyłącznie do zasobów, do których odczytywania posiada uprawnienia. Istnieje wiele technik, które umożliwiają zarządzanie dostępem do danych i w tym kursie poznasz najważniejsze z nich. Dowiesz się nie tylko w jaki sposób zabezpieczać dane ale poznasz też najpopularniejsze sposoby na to aby te zabezpieczenia łamać i obchodzić. Mając świadomość takiej możliwości, będziesz w stanie lepiej podejmować decyzje o tym jak projektować dostęp do informacji.
Istnieją problemy charakterystyczne dla niemal każdej aplikacji. W związku z ich powszechnością, istnieje też szereg rozwiązań lub sposobów na to, aby możliwie zredukować ich następstwa. Na przestrzeni lekcji tego kursu zobaczysz wiele przykładów, które pomogą Ci w codziennej pracy a niekiedy nawet, pozwolą uniknąć błędów, których konsekwencje nie zawsze występują natychmiast.
Kurs powstał z myślą o osobach, które chcą rozwijać się w roli full-stack web developera / developerki, przeprowadzając przez najważniejsze zagadnienia z obszaru wymiany, transformacji oraz przechowywania informacji w aplikacji oraz pomiędzy aplikacjami. Jeżeli potrzebujesz zobaczyć szeroką perspektywę tego, w jaki sposób dane przepływają pomiędzy różnymi częściami aplikacji, lub ugruntować swoją wiedzę na ten temat, to ten kurs jest miejscem, którego szukasz.
W tej lekcji przyjrzymy się bliżej tematowi statusów odpowiedzi, które
stanowią kolejne ważne elementy komunikacji naszej aplikacji i również
trzeba poświęcić im sporo uwagi w momencie projektowania naszej aplikacji.
Co ciekawe, statusy odpowiedzi w tym kontekście będą dotyczyć protokołu HTTP.
Natomiast nic nie stoi na przeszkodzie, aby zastosować statusy odpowiedzi w innym
miejscu i pokażę Ci też przykłady, gdzie można to dokładnie zrobić.
Zacznijmy od tego, że w przypadku statusów HTTP zasadniczo wyróżniamy ich 5 grup,
gdzie zwykle mamy do czynienia z błędami typu 100, 200, 300, 400, 500.
Jeżeli chodzi o status, to są one czysto
informacyjny i dotyczą sytuacji, w której serwer przyjmuje zapytanie, ale jego
przetworzenie trwa, a Ciebie tylko informuje o tym fakcie.
Takie statusy są bardzo rzadko spotykane i
osobiście nie kojarzę, abym miał z nimi do czynienia, a przynajmniej nie świadomie.
Następnie mamy statusy typu 200, które zasadniczo oznaczają, że wszystko jest w
porządku i operacja, którą chcieliśmy wykonać została wykonana i mamy odpowiedź.
Zaraz po nich mamy statusy,
które w skrócie informują nas o przekierowaniu, czyli np.
przekierowanie 301 informuje o tym, że
dany zasób jest dostępny pod nowym adresem i że ewentualnie tam możemy go odczytać.
Jednocześnie, jeżeli do takiej odpowiedzi dołączony jest jeszcze nagłówek Location,
przeglądarka automatycznie przekieruje Cię na nowy adres.
Następnie mamy statusy 400, które
informują użytkownika o tym, że coś poszło nie tak.
Inaczej ten błąd wynika z jego winy.
Statusy 500 dotyczą zwykle błędu, który my
popełniliśmy i w konsekwencji takie błędy kończą się nieprawidłowym działaniem
aplikacji bądź też nawet brakiem odpowiedzi serwera.
No i teraz analogicznie jak w przypadku metod HTTP.
Statusów HTTP jest odpowiednio więcej.
W tym jednak przypadku jest ich
zdecydowanie więcej niż metod HTTP, a to oznacza, że znowu musimy wybierać.
Dobra wiadomość jest taka, że nie musisz znać ich wszystkich.
I właśnie teraz zwrócimy uwagę na te najbardziej popularne.
Pierwszą kategorię, czyli statusy 200 oraz 201 wykorzystujemy w momencie, gdy
zapytanie zostanie zrealizowane i w większości przypadków jest to status 200.
Natomiast w momencie, gdy jakiś zasób
został utworzony, wysyłamy statusy 201, a zaraz po nich mamy statusy 301 oraz 302.
Są to główne statusy przekierowań, które wykorzystujemy na potrzeby stałego
przeniesienia jakiegoś zasobu bądź też tymczasowego przekierowania, które w
bliżej nieokreślonej przyszłości zostanie odwrócone.
Następnie mamy statusy błędów użytkownika.
Jest ich zdecydowanie więcej, ale po raz
kolejny to my decydujemy o tym, które będziemy wspierać w naszej aplikacji.
Po prostu obsługa każdego z nich to
dodatkowy wysiłek, które musimy podejmować.
I co prawda w niektórych przypadkach jest
on bardzo mały, ale w innych może wymagać od nas bardzo dużo uwagi.
Tak czy inaczej mamy tutaj błędy informujące o błędnym zapytaniu i zwykle
dotyczy to sytuacji, w której wysyłamy zapytanie i przesyłamy jakieś parametry,
które są po prostu niezgodne z tym co akceptuje serwer.
Czyli np. chcemy pobrać stronę o numerze minus 1.
Coś takiego bez wątpienia jest błędnym zapytaniem.
W związku z tym powinniśmy zwrócić błąd.
Następnie status 401 mówiący o tym, że nie jesteśmy zalogowani.
402 o tym, że wymagana jest płatność i
tutaj też możemy przekierować użytkownika do bramki płatności.
No bo trzeba zwrócić uwagę na fakt, że te
odpowiedzi nie są wysyłane przez serwer tylko dla samego faktu.
Mogą one być wykorzystane po stronie klienta po to, aby np.
w tym przypadku przekierować użytkownika
do wspomnianej bramki płatności, a w przypadku powyżej do strony logowania.
Idąc dalej mamy też status 403 mówiący o tym, że dostęp do zasobu jest zabroniony i
w tej sytuacji też możemy poinstruować użytkownika, aby np.
skontaktował się z administratorem w celu
ewentualnego przydzielenia odpowiednich uprawnień.
Status 404, czyli chyba najbardziej popularny, czyli not found informujący o
tym, że jakiś zasób nie został odnaleziony.
Z czymś takim mamy do czynienia albo w sytuacji, gdy zasób został usunięty, bądź
też gdy nigdy go nie było i z tego powodu, gdy serwer zwraca nam status 404, warto
poinformować użytkownika i przekierować go oraz która poinformuje go o tym fakcie.
Następnie 409 to status informujący o konflikcie.
I tutaj mamy do czynienia z sytuacjami, w której np.
użytkownik próbuje po raz kolejny
zarejestrować konto na ten sam adres email.
Teoretycznie w takiej sytuacji możemy wykorzystać status 400, czyli bad request,
natomiast jeżeli chcemy być bardziej precyzyjni, możemy wykorzystać 409.
Idąc dalej mamy jeszcze 413, czyli bailout
wykorzystywany głównie w sytuacji, gdy przesyłamy jakieś pliki i są one po prostu
zbyt duże do tego, abyśmy mogli je obsłużyć.
Podobnie też 422 obsługuje sytuacje, w
których po prostu z jakiegoś powodu nie możemy przetworzyć zapytania lub też 429 w
momencie, gdy chcemy poinformować użytkownika, że jego zapytanie.
Nie mogło zostać obsłużone ze względu na
to, że w danej chwili do serwera napływa zbyt wiele żądań.
Jak widzisz, większość tych błędów może
być tak naprawdę ograniczona do statusu 404, 101 i ewentualnie 404.
Wszystkie pozostałe mogą zostać pominięte, a to, które z nich będziesz wykorzystywać
ponownie, pozostawione jest Twojej decyzji oraz potrzebom Twojej aplikacji.
No i na sam koniec mamy jeszcze błędy 500,
w przypadku których również jest ich przynajmniej kilka.
Natomiast najczęściej mamy tutaj do czynienia z błędem 500, czyli server
error, w przypadku którego tak naprawdę użytkownik nie musi wiedzieć wiele więcej.
Jak najbardziej na tym etapie powinniśmy jak najwięcej zalogować po naszej stronie,
aby móc pomóc sobie w ten sposób w procesie naprawiania tego błędu.
Zatem tak wyglądają statusy odpowiedzi
jeżeli chodzi o teorię i mam nadzieję, że jest to dla Ciebie jasne.
Naturalnie zaznaczam tylko, że statusy
odpowiedzi wysyłane są przez serwer, a klient je tylko obsługuje.
Jednocześnie ich rola jest bardzo duża ze względu na to, że.
Wyobraź sobie, że użytkownik próbuje utworzyć nowy artykuł.
Ze względu na to, że jest niezalogowany, otrzymujemy odpowiedź o statusie 401,
która informuje nas o tym, aby przekierować go do strony logowania.
Następnie na tej stronie użytkownik podaje adres email o niewłaściwym formacie.
Z jakiegoś powodu nie wykonaliśmy tego na
pędzie, więc wysyłamy zapytanie na backend i tam otrzymujemy status 400.
Ponownie informujemy użytkownika o tym, że wystąpiły błędy i dodatkowo uzgadniamy
też, które pole jest nieprawidłowe, więc użytkownik je poprawia.
Następnie niestety jego konto już
istnieje, więc mamy do czynienia z konfliktem 410 i informujemy użytkownika,
że nie powinien się w tym momencie rejestrować, tylko możemy przekierować go
do strony logowania i tam ewentualnie może dostać się do aplikacji.
Otrzymuje status 200 i wtedy tworzy artykuł i otrzymuje status 201.
W tym wszystkim chodzi o to, że w momencie
gdy zwraca nam odpowiedź to te odpowiedzi posiadają np.
wiadomość, którą przesyłamy do użytkownika
bądź inny zestaw informacji, które możemy wykorzystać po stronie frontendu.
Jednocześnie jak pamiętamy, to backend powinien być naszym.
Gdzie to było naprawdę?
Oznacza to, że to z serwera powinna pochodzić informacja o tym, że coś poszło
nie tak i ewentualnie o tym jaki błąd mamy wyświetlić.
Natomiast na podstawie samej informacji o błędzie, czyli np.
błędnej email, nie powinniśmy raczej
podejmować żadnych działań po stronie frontendu.
W zamian moglibyśmy wykorzystać status 400 do tego, aby poinformować użytkownika, że
jest jakiś błąd, a dodatkowo w samym ciele odpowiedzi powinna być uwzględniona np.
tablica błędów z informacją o tym, że input o kluczu email zawiera błąd, w
przypadku którego powinniśmy wyświetlić taki a nie inny komunikat.
Jeżeli chodzi o praktykę zastosowania
tego, co właśnie powiedziałem, o tym będziemy jeszcze rozmawiać.
Najważniejsze jest jednak to, aby było dla
Ciebie jasne, że statusów HTTP jest naprawdę dużo, a te, które wymieniłem
tutaj są tymi, które zasługują na Twoją szczególną uwagę.
Jednocześnie myślę, że warto, abyśmy teraz
spojrzeli, jak możemy wykorzystać je po stronie frontendu i backendu.
Zatem jeżeli przejdziemy
do inteligencja, to powiedzmy, że mamy tutaj prostą aplikację, która umożliwia
wysłanie zapytania GET, która zwróci nam prosty ciąg znaków.
Zatem jeżeli teraz wyśle takie zapytanie, to nie jest
zaskoczeniem, że taki ciąg znaków tutaj otrzymuję.
Jednocześnie zobacz, że możemy sobie tutaj
podejrzeć nagłówki i jest ich całkiem sporo.
I co więcej, mamy tutaj chociażby nasz
content type, który poprawnie został ustawiony na text html.
Oznacza to, że ktoś wykonał tutaj sporo pracy za mnie.
Pierwszym odpowiedzialnym jest oczywiście MS, że jest genialne, aczkolwiek
oczywiście nie bezbłędny sposób próbuje nam pomagać na każdym kroku.
Tutaj dodatkowo mam jeszcze zainstalowany
Helmet, tutaj aktywowałem wcześniej i zobaczę Jeżeli go teraz wyłączę i ponownie
wykonam to samo zapytanie to jest tutaj tylko 7 nagłówków w porównaniu do aż 19,
które są ustawione automatycznie w momencie gdy mamy aktywny Helmet.
A trzeba jeszcze dodać, że nic nie stoi na przeszkodzie, aby dodatkowo skonfigurować
niektóre z nich, aby jeszcze bardziej chroniły naszą aplikację.
Zatem teraz nie ulega wątpliwości fakt, że
wykorzystanie Hermesa to coś, co absolutnie powinniśmy robić.
Przejdźmy teraz dalej, do drugiego
zapytania, które będzie odpowiedzialne początkowo za przesłanie samego imienia,
które zostanie wykorzystane po stronie backendu.
Mianowicie wewnątrz kontrolera utworzymy drugą metodę, która w odróżnieniu od
poprzednich będzie reagować na metodę POST.
Mamy dokładnie ten sam adres, tylko inną akcję.
Ta akcja będzie przechwytywać z ciała
zapytania imię użytkownika, a następnie zwracać tekst.
Jeżeli więc wyśle zapytanie, to mamy tutaj
odpowiedź, która bez wątpienia wykorzystuje przesłane tutaj dane.
Jednocześnie, tak jak przed chwilą wspomniałem.
Na każdym kroku próbuje nam pomóc.
Tak, w tym momencie mamy tutaj status 201 informujący o utworzeniu jakiegoś zasobu.
Coś takiego oczywiście nie miało miejsca.
Z tego powodu dobrym pomysłem będzie, abyśmy to naprawili.
Aby to zrobić musimy nadpisać natywne zachowanie Message poprzez wyciągnięcie
tutaj obiektu response, który działa podobnie jak request, z tą różnicą, że
request oczywiście przechowuje informacje na temat zapytania.
A tutaj możemy skonfigurować sobie odpowiedź i jeżeli pójdziemy dalej to
wystarczy, że zamienimy to na metodę reset, czyli wysłanie
odpowiedzi, do której przekażemy naszą zawartość, a następnie odwołamy się też do
metody Status, w przypadku której ustawimy go na 200.
Jeżeli teraz wykonamy nasze zapytanie
ponownie, to nie stanowi zaskoczenia fakt, że mamy tutaj już poprawny status i jest
to informacja dla nas, że to my odpowiadamy za to, jakie statusy oraz
nagłówki są ustawiane zarówno w zapytaniach, jak i odpowiedziach.
W tym przypadku akurat było to stosunkowo
łatwe, aczkolwiek w przypadku większej aplikacji warto byłoby zadbać np.
o to, aby status 200 nie był wpisywany
ręcznie, tylko był przypisany do jakiejś stałej, która w sposób bardzo precyzyjny
określi, kiedy po niego sięgamy, a kiedy korzystamy z innych statusów.
Jeżeli chodzi o praktykę w tym zakresie,
to będziemy sobie o tym jeszcze wielokrotnie mówić, więc na razie to
zostawmy i przejdźmy teraz do jeszcze jednego przykładu.
Tutaj również będzie odpowiadać na
zapytanie typu post, z tą różnicą, że będziemy sobie rejestrować użytkownika.
W tym przypadku do naszego zapytania
przekażemy nie tylko jego imię, ale również adres.
No i w momencie gdy takie zapytanie
zostanie wykonane, użytkownik zostanie utworzony.
Ale jak pamiętasz mamy tam podłączoną
pryzmę, a to oznacza, że drugie wykonanie tego samego zapytania zakończy się błędem.
I co ciekawe jest to błąd serwera ze względu na to, że w żaden sposób nie
obsłuży wyjątku, który w tym momencie zwracany jest przez pryzmę.
Oznacza to, że jeżeli zatrzymamy sobie serwer developerski i w
zamian przejdziemy do pakiet Jackson i aktywujemy tutaj debuggera, no to na tym
etapie będziemy mogli sobie sprawdzić, co tutaj dokładniej się dzieje.
Przede wszystkim, jeżeli teraz wyślemy ponownie zapytanie, znowu otrzymamy błąd
500, ale w konsoli możemy podejrzeć sobie co tutaj dokładniej się dzieje.
Faktycznie mamy błąd zwrócony przez pryzmat informujący nas o tym, że w
dokumencie user doszło do naruszenia reguły unikatowości.
W przypadku właściwości email to oznacza,
że musimy dodawać tutaj odpowiednią obsługę błędów.
Zacznijmy od tego, że umieścimy tutaj blok pakietu, a następnie wyrzucimy wyjątek.
Sytuacja w tym momencie różni się już na
tyle, że jeżeli wykonamy to zapytanie ponownie, otrzymamy tutaj błąd 409.
Rzecz w tym, że jeżeli przekażemy tutaj liczbę, no to ponownie otrzymaliśmy tutaj
błąd 409, aczkolwiek wiemy, że nie jest to prawda i za chwilę Ci to udowodnię.
Mianowicie w bloku catch umieścimy sobie
debugger i teraz jeżeli ponownie wykonamy to zapytanie to nasza aplikacja się
zatrzyma i mamy tutaj informację o błędzie typu.
W związku z tym warto byłoby zadbać o to, aby obsługiwać taki rodzaj błędów.
No i teraz być może zastanawiasz się jak to w ogóle obsłużyć.
Przede wszystkim jeżeli zatrzymamy naszą aplikację w tym miejscu, to zobaczymy, że
w momencie wysłania zapytania otrzymujemy tutaj błąd, który nie za dużo nam mówi.
Po pierwsze mamy tutaj informacje, które możemy wykorzystać na potrzeby naszych
logów, aczkolwiek raczej nie możemy zwrócić tego użytkownikowi.
No chyba, że wykorzystamy tutaj jakieś do tego, aby wyciągnąć pole.
Co za błąd i w jaki sposób poinformować
użytkownika o tym, co tutaj dokładnie się wydarzyło.
Jednocześnie jeżeli zajrzymy do dokumentacji,
to zobaczymy, że rzeczywiście zwraca ona nie tylko informacja o błędach, ale
również ma statusy tych błędów, czyli inaczej mówiąc kody błędów.
Swoją drogą jest to dokładnie scenariusz, w przypadku którego warto zdefiniować
sobie takie kody, aby na ich podstawie rozpoznawać to, z jakim błędem mamy do
czynienia i też odpowiednio obsłużyć Dokładnie to za chwilę.
Ale jeżeli teraz zajrzymy do informacji na temat błędów walidacji, to zobaczymy, że
jedyną informacją, którą tutaj otrzymujemy jest właściwość Message.
Oznacza to, że w przypadku błędów
walidacji nie jesteśmy w stanie posługiwać się kodami błędów.
Jest to dla nas szczególnie istotna
informacja ze względu na to, że możemy ją wykorzystać w naszym bloku catch.
Mianowicie przede wszystkim musimy zastosować sobie tutaj tablicę, która
weryfikuje czy mamy do czynienia z błędem walidacji.
W tym przypadku faktycznie tak jest, więc możemy wyrzucić sobie wyjątek Bad request,
w przypadku którego przekażemy informację o tym, że nie można utworzyć użytkownika.
Aczkolwiek tak jak wspomniałem, możesz tutaj stworzyć jakiś pecet do tego, aby
przygotować bardziej precyzyjne informacje na temat tego, co się wydarzyło.
Jeżeli spróbujemy ponownie wysłać zapytanie, no to mamy tutaj status 400.
Oraz informacja o tym, że użytkownik nie mógł zostać utworzony.
Jeżeli jednak w tym przypadku wrócimy
sobie do sytuacji, którą mieliśmy wcześniej, to nie otrzymamy żadnej
informacji o błędzie, pomimo tego, że taki wystąpił.
Wynika to z faktu, że pomimo tego, że
zapisaliśmy sobie tutaj blok catch to niestety obsługujemy tylko jedną sytuację.
Z tego powodu jeżeli teraz utworzymy sobie
drugi palec i odwołamy się tym razem do klasy, która pozwala nam dotrzeć do
informacji o kodzie błędu, to jesteśmy w stanie sprawdzić, że ten konkretnie numer
odwołuje się do naruszenia unikatowości, z którą mamy do czynienia właśnie w
przypadku tworzenia podwójnego adresu email.
Oznacza to, że możemy tutaj zwrócić ponownie informację do użytkownika.
W tym przypadku dość genetyczną, ale jednocześnie ponownie możemy zadbać o to,
aby poinformować go nieco bardziej precyzyjnie o tym, co się wydarzyło.
No i teraz ostatecznie możemy tutaj zwrócić albo serwer error, albo błąd
serwera, albo genetyczną informację na temat błędu, który wystąpił, który
poinformuje użytkownika, że coś poszło nie tak, ale raczej możemy nie być w stanie
precyzyjnie określić, co dokładnie się tam wydarzyło.
Ostatecznie, jeżeli utworzymy sobie teraz
tego użytkownika, mamy tutaj błąd informujący o konflikcie.
W przypadku błędu typu mamy błąd
genetyczny, który w tym przypadku pochodzi dokładnie z tego wyjątku.
I teraz to, co właśnie robimy jasno pokazuje Ci jak wymagająca jest obsługa
błędów i wszystkich wyjątków, które występują po stronie serwera.
Z tego powodu warto korzystać chociażby z możliwości, które dają nam frameworki i
też narzędzia takie jak Helmet, które pomagają w konfiguracji tego wszystkiego.
Ale jednocześnie też warto opracować sobie
techniki, które będą w stanie zaadresować takie sytuacje globalnie.
Oznacza to, że możemy utworzyć tzw.
global chair, po której będziemy mogli sięgnąć za każdym razem, gdy np.
będziemy tworzyć nowe rekordy w bazie
i myślę, że w tym miejscu możemy się zatrzymać.
Naturalnie wszystkie te struktury
dotyczące tworzenia endpoint oraz wszystkich technik, o których przed chwilą
wspomniałem, będziemy poruszać też nieco później.
Natomiast na tą chwilę mam nadzieję, że jest dla Ciebie jasne to, jakie zadanie
stoi przed Tobą w momencie projektowania backendu, ponieważ to jak dobrze to
zrobisz będzie zależało od tego jak łatwo obsłużyć informacje po stronie klienta.
Teraz dzięki za uwagę i zapraszam do kolejnej lekcji.