net/http.Request în Go

Structura unei cereri HTTP: metoda, URL-ul, antetele, corpul și contextul (Context()) care se anulează când clientul pleacă.

Sintaxă
type Request struct {
    Method string
    URL    *url.URL
    Header Header
    Body   io.ReadCloser
    Host   string
    RemoteAddr string
    Form   url.Values
    ...
}
func (r *Request) Context() context.Context

Ce face net/http.Request

Pe server, handler-ul primește r *http.Request cu tot ce a trimis clientul: r.Method (GET, POST…), r.URL.Path și r.URL.Query(), antetele în r.Header, corpul în r.Body (un flux pe care îl citești o singură dată), adresa clientului în r.RemoteAddr. Pe client, aceeași structură descrie cererea pe care o trimiți (o creezi cu http.NewRequest).

r.Context() returnează contextul cererii. Pe server, acesta se anulează când clientul închide conexiunea sau cererea se termină; dă-l mai departe interogărilor și apelurilor lente, ca să nu lucrezi degeaba pentru un client care a plecat. Pe client, este contextul dat la NewRequestWithContext.

Ce returnează

Context() returnează un context.Context (niciodată nil).

Exemple

Exemplul 1: Câmpurile unei cereri

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
)

func main() {
	arata := func(w http.ResponseWriter, r *http.Request) {
		corp, _ := io.ReadAll(r.Body)
		fmt.Println("Metoda:  ", r.Method)
		fmt.Println("Cale:    ", r.URL.Path)
		fmt.Println("Query:   ", r.URL.Query())
		fmt.Println("Host:    ", r.Host)
		fmt.Println("Agent:   ", r.Header.Get("User-Agent"))
		fmt.Println("Tip corp:", r.Header.Get("Content-Type"))
		fmt.Println("Corp:    ", string(corp))
	}
	req := httptest.NewRequest("PUT", "http://magazin.local/produse/17?moneda=lei",
		strings.NewReader(`{"pret": 32.5}`))
	req.Header.Set("User-Agent", "aplicatie-mobila/2.1")
	req.Header.Set("Content-Type", "application/json")
	arata(httptest.NewRecorder(), req)
}

Rezultat

Metoda:   PUT
Cale:     /produse/17
Query:    map[moneda:[lei]]
Host:     magazin.local
Agent:    aplicatie-mobila/2.1
Tip corp: application/json
Corp:     {"pret": 32.5}

Exemplul 2: Contextul cererii: clientul a renunțat

Clientul are răbdare 50 ms. Handler-ul urmărește r.Context() și se oprește când conexiunea este închisă.

package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"
)

func main() {
	oprit := make(chan error, 1)
	lent := func(w http.ResponseWriter, r *http.Request) {
		select {
		case <-time.After(2 * time.Second):
			fmt.Fprint(w, "raport gata")
			oprit <- nil
		case <-r.Context().Done():
			oprit <- r.Context().Err()
		}
	}
	srv := httptest.NewServer(http.HandlerFunc(lent))
	defer srv.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
	defer cancel()
	req, _ := http.NewRequestWithContext(ctx, "GET", srv.URL, nil)
	_, err := http.DefaultClient.Do(req)
	fmt.Println("Clientul a primit:", err != nil)
	fmt.Println("Handler-ul s-a oprit:", <-oprit)
}

Rezultat

Clientul a primit: true
Handler-ul s-a oprit: context canceled

Greșeli frecvente

  • Citești r.Body de două ori (de exemplu o dată pentru jurnal, apoi pentru JSON). A doua citire este goală; citește o dată în []byte și folosește copia.
  • Folosești context.Background() în handler pentru interogări: dacă clientul pleacă, interogarea continuă degeaba. Folosește r.Context().
  • Te bazezi pe r.RemoteAddr pentru IP-ul real în spatele unui proxy (Nginx, Cloudflare): este adresa proxy-ului.

Comenzi înrudite

  • net/http.Request.FormValue: FormValue citește un câmp dintr-un formular sau din query string; ParseForm le citește pe toate în r.Form și r.PostForm.
  • net/http.Request.PathValue: Returnează valoarea unui parametru din calea rutei, de exemplu id din modelul „/produse/{id}”.
  • net/http.Header: Antetele unei cereri sau ale unui răspuns: Get citește, Set înlocuiește, Add adaugă încă o valoare, Del șterge.
  • net/http.NewRequest: Construiește o cerere HTTP completă (orice metodă, antete, corp) care se trimite apoi cu Client.Do.
  • context.Context: Interfața care duce prin program semnalul de anulare, termenul limită și valori legate de o cerere (ctx).