Interfejs http.Handler
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 77 z 107.
Interfejs http.Handler stanowi podstawę architektury serwerów internetowych w Go. Określa sposób przetwarzania żądań HTTP, a każdy typ, który go implementuje, może obsługiwać żądania internetowe.
Interfejs jest niezwykle prosty:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Każdy typ z metodą ServeHTTP może obsługiwać żądania HTTP. Oto własny program obsługi:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Możesz użyć tego modułu obsługi bezpośrednio z serwerem:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)W prostszych przypadkach Go udostępnia http.HandlerFunc, typ, który pozwala przekształcić zwykłą funkcję w procedurę obsługi:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Potęgę tego interfejsu dostrzeżesz, gdy uświadomisz sobie, że procedury obsługi mogą opakowywać inne procedury obsługi. Umożliwia to stosowanie wzorców oprogramowania pośredniczącego, dzięki którym możesz dodawać rejestrowanie, uwierzytelnianie lub inne zagadnienia przekrojowe bez modyfikowania oryginalnej logiki procedury obsługi.
Wyzwanie
ŁatwyStwórzmy prosty system obsługi HTTP, który symuluje sposób przetwarzania żądań internetowych! Ponieważ w tym środowisku nie możemy uruchomić prawdziwego serwera HTTP, utworzysz procedury obsługi implementujące interfejs http.Handler i pokażesz, jak przetwarzają żądania, korzystając z obiektów żądania i odpowiedzi będących atrapami.
Rozmieścisz swój kod w dwóch plikach:
handlers.go: Zdefiniuj własne procedury obsługi HTTP.Utwórz strukturę
MockResponseWriter, która przechwytuje to, co zostałoby zapisane w odpowiedzi HTTP. Powinna przechowywać treść odpowiedzi jako ciąg znaków i implementować metodęWrite(data []byte) (int, error), która dopisuje dane do treści. Dodaj metodęBody() string, aby pobierać zgromadzoną odpowiedź.Utwórz strukturę
MockRequestz polami tekstowymiPathiMethod, aby symulować żądanie HTTP.Teraz utwórz dwie struktury procedur obsługi, które implementują wzorzec interfejsu
http.Handler(z metodąServeHTTP, która przyjmuje*MockResponseWriteri*MockRequest):WelcomeHandlerz polemMessage— zapisuje wiadomość, a po niej ścieżkę żądaniaMethodHandler— zapisuje różne odpowiedzi w zależności od metody żądania:"Fetching data"dla GET,"Creating resource"dla POST i"Method not supported"dla każdej innej metody
main.go: Przetwarzaj żądania za pomocą procedur obsługi.Odczytaj typ procedury obsługi (
welcomelubmethod), a następnie odczytaj szczegóły żądania. W przypadku procedury obsługi powitania odczytaj również wiadomość powitalną.Utwórz odpowiednią procedurę obsługi, zbuduj atrapę żądania i wywołaj
ServeHTTP, aby je przetworzyć. Wypisz treść odpowiedzi.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Typ procedury obsługi (
welcomelubmethod) - Wiersz 2: Ścieżka żądania
- Wiersz 3: Metoda żądania
- Wiersz 4 (tylko dla welcome): Wiadomość powitalna
Na przykład dla danych:
welcome
/home
GET
Hello fromWynik powinien być następujący:
Hello from /homeA dla danych:
method
/api/users
POSTWynik powinien być następujący:
Creating resourceA dla danych:
method
/api/data
GETWynik powinien być następujący:
Fetching dataA dla danych:
method
/api/items
DELETEWynik powinien być następujący:
Method not supportedTo wyzwanie pokazuje podstawową koncepcję interfejsu http.Handler: każdy typ mający metodę ServeHTTP może obsługiwać żądania. W rzeczywistych aplikacjach http.ResponseWriter i *http.Request pochodzą ze standardowej biblioteki Go, ale wzorzec implementowania procedur obsługi pozostaje dokładnie taki sam.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Odczytaj typ procedury obsługi
scanner.Scan()
handlerType := scanner.Text()
// Odczytaj ścieżkę żądania
scanner.Scan()
path := scanner.Text()
// Odczytaj metodę żądania
scanner.Scan()
method := scanner.Text()
// Utwórz atrapę żądania
request := &MockRequest{
Path: path,
Method: method,
}
// Utwórz atrapę obiektu zapisującego odpowiedź
response := &MockResponseWriter{}
// TODO: Na podstawie handlerType utwórz odpowiednią procedurę obsługi
// Jeśli handlerType ma wartość "welcome", odczytaj wiadomość powitalną i utwórz WelcomeHandler
// Jeśli handlerType ma wartość "method", utwórz MethodHandler
// Wywołaj ServeHTTP na procedurze obsługi, przekazując response i request
if handlerType == "welcome" {
scanner.Scan()
message := scanner.Text()
// TODO: Utwórz WelcomeHandler z wiadomością i wywołaj ServeHTTP
_ = message // Usuń ten wiersz podczas implementacji
} else if handlerType == "method" {
// TODO: Utwórz MethodHandler i wywołaj ServeHTTP
}
// Wypisz treść odpowiedzi
fmt.Println(response.Body())
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików11Biblioteka standardowa i OOP
io.Reader i io.Writersort.InterfaceInterfejs fmt.Stringerencoding/json ze strukturamiInterfejs http.HandlerPowtórzenie – modele REST API3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online