Obsługa żądań POST i plików w Go: praktyczny przewodnik dla programistów
Druga część cyklu poświęconego net/http dotyczy obsługi żądań POST, przesyłania plików oraz tworzenia pamięciowego magazynu. Te umiejętności są kluczowe dla tworzenia bezpiecznych i wydajnych serwisów internetowych w Go.
Podstawy obsługi żądań POST
Żądania POST służą do tworzenia nowych zasobów na serwerze. W przeciwieństwie do GET, dane są przesyłane w ciele żądania, co pozwala na wysyłanie dużych ilości informacji, w tym plików. Kluczowe aspekty obsługi:
- Sprawdzanie metody HTTP poprzez r.Method
- Używanie stałych net/http dla poprawy czytelności kodu
- Ograniczanie rozmiaru ciała żądania, aby zapobiec atakom DoS
- Prawidłowa obsługa różnych typów kodowania danych
Przykład podstawowego sprawdzania metody:
func CreateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Dalsza obsługa
}
Praca z formularzami i danymi
Formularze mogą używać dwóch głównych typów kodowania: multipart/form-data dla plików i application/x-www-form-urlencoded dla danych tekstowych. Do parsowania form multipart stosuje się metodę ParseMultipartForm:
err := r.ParseMultipartForm(32 << 20) // Ograniczenie 32 MB
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
Wyodrębnianie wartości tekstowych odbywa się poprzez FormValue lub PostFormValue:
- r.FormValue("message") — zwraca wartość pola formularza
- r.PostFormValue("message") — analogicznie, ale ignoruje parametry zapytania
Ważne cechy:
- Metody zwracają pierwszą wartość dla podanego klucza
- Przy pustym polu zwracany jest pusty ciąg
- FormValue działa z obydwoma typami kodowania
Bezpieczna obsługa plików
Przesyłanie plików wymaga szczególnej uwagi na bezpieczeństwo. Metoda FormFile zwraca plik, jego nagłówek i ewentualny błąd:
file, fileHeader, err := r.FormFile("upload")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
FileHeader zawiera metadane pliku:
- Filename — nazwa pliku
- Size — deklarowany rozmiar
- Header — typ MIME i inne informacje
Krytyczne środki bezpieczeństwa:
- Walidacja typów MIME
buf := make([]byte, 512)
_, err = file.Read(buf)
if err != nil {
http.Error(w, "Cannot read file", http.StatusInternalServerError)
return
}
mimeType := http.DetectContentType(buf)
allowedTypes := []string{"image/jpeg", "image/png", "application/pdf"}
// Sprawdzenie zgodności z dozwolonymi typami
- Sanacja nazw plików
safeFileName := filepath.Base(fileHeader.Filename)
// Zapobiega atakom path traversal
- Ograniczenie rozmiaru plików
r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10 MB
Tworzenie pamięciowego magazynu
Pamięciowy magazyn zapewnia szybki dostęp do danych, ale wymaga zarządzania pamięcią i zapewnienia bezpieczeństwa wątków. Podstawowa implementacja obejmuje:
- Użycie sync.Map dla dostępu współbieżnego
- Implementację TTL (Time To Live) do automatycznego usuwania przestarzałych danych
- Hashowanie wrażliwych informacji
Przykład struktury magazynu:
type Storage struct {
data sync.Map
mu sync.RWMutex
}
func (s *Storage) Set(key string, value interface{}, ttl time.Duration) {
s.mu.Lock()
defer s.mu.Unlock()
s.data.Store(key, value)
// Ustawienie timera do usunięcia po upływie TTL
}
Kluczowe zalety podejścia pamięciowego:
- Ekstremalnie wysoka prędkość dostępu
- Łatwość implementacji i debugowania
- Idealne dla danych tymczasowych i buforowania
Ograniczenia:
- Dane są tracone przy restarcie aplikacji
- Ograniczone dostępną pamięcią RAM
- Wymaga implementacji mechanizmów oczyszczania
Optymalizacja wydajności
Aby zapewnić stabilną pracę pod obciążeniem, należy:
- Ustalać rozsądne limity rozmiaru żądań i plików
- Używać pul buforów do pracy z plikami
- Implementować graceful shutdown do zapisywania danych
- Monitorować użycie pamięci i na czas oczyszczać pamięć podręczną
Przykład konfiguracji limitów:
server := &http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20, // 1 MB
}
Co jest ważne
- Zawsze sprawdzaj metodę HTTP i zwracaj odpowiednie statusy błędów
- Ograniczaj rozmiar żądań i plików, aby chronić przed atakami DoS
- Waliduj typy MIME przesyłanych plików
- Stosuj sanację nazw plików, aby zapobiec path traversal
- Implementuj mechanizmy oczyszczania pamięciowego magazynu
- Testuj obsługę błędów i przypadki brzegowe
Podsumowanie
Obsługa żądań POST i plików to fundamentalna umiejętność dla programistów Go. Prawidłowa implementacja tych mechanizmów zapewnia bezpieczeństwo, wydajność i niezawodność serwisów internetowych. Pamięciowy magazyn uzupełnia to, zapewniając szybki dostęp do danych tymczasowych. W kolejnych częściach cyklu omówione zostaną wzorce architektoniczne i mechanizmy uwierzytelniania.
— Editorial Team
Brak komentarzy.