podsumowanie obsługi żądań w Go
przetwarzanie żądań HTTP w Go to przede wszystkim dwie rzeczy: Serwemuksy i obsługa.
ServeMux jest zasadniczo routerem żądania HTTP (lub multipleksorem). Porównuje przychodzące żądania z listą wstępnie zdefiniowanych ścieżek URL i wywołuje skojarzony moduł obsługi ścieżki, gdy tylko znajdzie się dopasowanie.
manipulatory są odpowiedzialne za pisanie nagłówków odpowiedzi i ciał. Prawie każdy obiekt może być Obsługą, o ile spełnia interfejshttp.Handler
. W kategoriach lay oznacza to po prostu, że musi mieć ServeHTTP
metoda z następującym podpisem:
ServeHTTP(http.ResponseWriter, *http.Request)
pakiet HTTP Go zawiera kilka funkcji do generowania popularnych programów obsługi, takich jak FileServer
NotFoundHandler
I RedirectHandler
. Zacznijmy od prostego, ale wymyślnego przykładu:
przejdźmy przez to szybko:
- w funkcji
main
używamy funkcjihttp.NewServeMux
aby utworzyć pusty servemux. - następnie używamy funkcji
http.RedirectHandler
, aby utworzyć nową funkcję obsługi. Ten moduł obsługi 307 przekierowuje wszystkie otrzymane żądania dohttp://example.org
. - następnie używamy funkcji
mux.Handle
, aby zarejestrować to w naszym nowym ServeMux, więc działa jako Obsługa wszystkich przychodzących żądań ze ścieżką URL/foo
. - w końcu tworzymy nowy serwer i zaczynamy nasłuchiwać przychodzących żądań za pomocą funkcji
http.ListenAndServe
, przekazując nasz ServeMux, aby dopasować żądania.
uruchom aplikację:
i odwiedźhttp://localhost:3000/foo
w swojej przeglądarce. Powinieneś zauważyć, że twoje żądanie zostanie pomyślnie przekierowane.
ktoś z Was mógł zauważyć coś ciekawego: sygnatura funkcji ListenAndServe toListenAndServe(addr string, handler Handler)
, ale jako drugi parametr przekazaliśmy ServeMux.
udało nam się to zrobić, ponieważ Typ ServeMux ma również metodę ServeHTTP
, co oznacza, że również spełnia interfejs obsługi.
dla mnie upraszcza to myślenie o ServeMux jako o specjalnym rodzaju obsługi, która zamiast dać odpowiedź sama przekazuje żądanie do drugiego obsługi. To nie jest tak duży skok, jak się wydaje-łączenie łańcuchów razem jest dość powszechne w Go.
niestandardowe programy obsługi
stwórzmy niestandardowy program obsługi, który odpowiada bieżącym czasem lokalnym w danym formacie:
dokładny kod tutaj nie jest zbyt ważny.
wszystko, co naprawdę ma znaczenie, to to, że mamy obiekt (w tym przypadku jest totimeHandler
struct, ale równie dobrze może to być łańcuch lub funkcja lub cokolwiek innego) i zaimplementowaliśmy metodę z podpisemServeHTTP(http.ResponseWriter, *http.Request)
na nim. To wszystko, czego potrzebujemy, by zostać opiekunem.
osadzmy to w konkretnym przykładzie:
w funkcji main
zainicjowaliśmy timeHandler
w dokładnie taki sam sposób, jak każdy normalny struct, używając &
symbol dający wskaźnik. Następnie, podobnie jak w poprzednim przykładzie, używamy funkcjimux.Handle
, aby zarejestrować to w naszym ServeMux.
teraz, gdy uruchomimy aplikację, ServeMux przekaże każde żądanie dla /time
prosto do naszej timeHandler.ServeHTTP
metody.
śmiało i spróbuj: http://localhost:3000/time
.
zauważ również, że możemy łatwo użyć timeHandler
w wielu trasach:
działa jako programy obsługi
dla prostych przypadków (jak w powyższym przykładzie) definiowanie nowych niestandardowych typów i metod servehttp wydaje się nieco gadatliwe. Przyjrzyjmy się alternatywnemu podejściu, w którym wykorzystujemy Go http.HandlerFunc
, aby zmusić normalną funkcję do spełnienia interfejsu obsługi.
każda funkcja, która ma podpis func(http.ResponseWriter, *http.Request)
może zostać przekonwertowana na typ HandlerFunc. Jest to użyteczne, ponieważ obiekty HandleFunc posiadają wbudowaną metodę ServeHTTP
, która – dość sprytnie i wygodnie – wykonuje zawartość oryginalnej funkcji.
jeśli brzmi to myląco, spróbuj spojrzeć na odpowiedni kod źródłowy. Zobaczysz, że jest to bardzo zwięzły sposób na to, aby funkcja spełniała interfejs obsługi.
odtwarzajmy aplikację timeHandler używając tej techniki:
w rzeczywistości konwersja funkcji do typu HandlerFunc, a następnie dodanie jej do ServeMux w ten sposób jest tak powszechne, że go zapewnia skrót:mux.HandleFunc
metoda.
tak wyglądałaby funkcja main()
, gdybyśmy zamiast tego użyli tego skrótu:
większość czasu używa funkcja obsługi jak to działa dobrze. Ale istnieje pewne ograniczenie, gdy sprawy zaczynają się coraz bardziej skomplikowane.
prawdopodobnie zauważyłeś, że w przeciwieństwie do metody wcześniej musieliśmy zakodować format czasu w funkcji timeHandler
. Co się dzieje, gdy chcemy przekazać informacje lub zmienne z main()
do modułu obsługi?
dobrym podejściem jest umieszczenie naszej logiki obsługi w zamknięciu i zamknięcie nad zmiennymi, których chcemy użyć:
funkcjatimeHandler
ma teraz subtelnie inną rolę. Zamiast zmuszać funkcję do obsługi (tak jak robiliśmy to wcześniej), teraz używamy jej do zwracania obsługi. Są dwa kluczowe elementy, aby to zadziałało.
najpierw tworzyfn
, anonimową funkcję, która uzyskuje dostęp do‐ lub zamyka zmiennąformat
tworząc zamknięcie. Niezależnie od tego, co zrobimy z zamknięciem, zawsze będzie mógł uzyskać dostęp do zmiennych lokalnych do zakresu, w którym został utworzony-co w tym przypadku oznacza, że zawsze będzie miał dostęp do zmiennej format
.
Po drugie nasze zamknięcie ma podpis func(http.ResponseWriter, *http.Request)
. Jak zapewne pamiętasz z poprzedniego, oznacza to, że możemy go przekonwertować na typ HandlerFunc (tak, że spełnia on interfejs obsługi). Nasza funkcjatimeHandler
zwraca to przekonwertowane zamknięcie.
w tym przykładzie właśnie przekazywaliśmy prosty łańcuch do modułu obsługi. Ale w rzeczywistym świecie aplikacji można użyć tej metody do przekazania połączenia z bazą danych, mapy szablonu lub dowolnego innego kontekstu na poziomie aplikacji. Jest to dobra alternatywa dla korzystania ze zmiennych globalnych i ma dodatkową zaletę tworzenia samodzielnych programów obsługi do testowania.
Możesz również zobaczyć ten sam wzorzec zapisany jako:
lub używając domyślnej konwersji do typu HandlerFunc po powrocie:
Defaultservemux
Prawdopodobnie widziałeś defaultservemux wymienione w wielu miejscach, od najprostszych przykładów Hello World do kodu źródłowego Go.
dużo czasu zajęło mi uświadomienie sobie, że to nic specjalnego. DefaultServeMux to zwykły stary ServeMux, jakiego już używaliśmy, który domyślnie tworzy instancję, gdy używany jest pakiet HTTP. Oto odpowiednia linia ze źródła Go:
var DefaultServeMux = NewServeMux()
generalnie nie powinieneś używać DefaultServeMux, ponieważ stwarza to zagrożenie bezpieczeństwa.
ponieważ DefaultServeMux jest przechowywany w zmiennej globalnej, każdy pakiet jest w stanie uzyskać do niego dostęp i zarejestrować trasę – w tym wszystkie pakiety innych firm importowane przez aplikację. Jeśli jeden z tych pakietów jest zagrożony, mogą użyć DefaultServeMux do ujawnienia szkodliwego programu obsługi w sieci.
więc z reguły dobrym pomysłem jest unikanie DefaultServeMux, a zamiast tego używanie własnego lokalnego ServeMux, tak jak do tej pory. Ale jeśli zdecydujesz się go użyć…
pakiet HTTP zawiera kilka skrótów do pracy z DefaultServeMux: http.Uchwyt i http.HandleFunc. Działają one dokładnie tak samo, jak ich funkcje imienne, na które już patrzyliśmy, z tą różnicą, że dodają funkcje obsługi do DefaultServeMux zamiast tych, które utworzyłeś.
dodatkowo, ListenAndServe powróci do użycia DefaultServeMux, jeśli nie zostanie dostarczony żaden inny moduł obsługi (to znaczy, że drugi parametr jest ustawiony na nil
).
więc jako ostatni krok, zaktualizujmy naszą aplikację timeHandler, aby zamiast tego użyć DefaultServeMux:
Jeśli podobał Ci się ten post na blogu, nie zapomnij sprawdzić mojej nowej książki o tym, jak budować profesjonalne aplikacje internetowe z Go!
Śledź mnie na Twitterze @ajmedwards.
wszystkie fragmenty kodu w tym poście są dostępne za darmo na licencji MIT.