Spune exact ce vrei să spui
Un API REST vorbește prin două vocabulare. Metoda spune ce vrea clientul: GET citește, POST creează, PUT înlocuiește, PATCH schimbă o parte, DELETE șterge. Codul de stare spune cum a decurs: 2xx succes, 4xx greșeala clientului, 5xx a serverului.
Un detaliu care merită știut: PUT este idempotent — trimis de două ori, se termină la fel ca o singură dată. POST nu este: două cereri POST creează două resurse.
Metoda potrivită
Tiparele de metodă din mux trimit fiecare verb către propriul handler. Crearea răspunde cu 201 Created și indică noua resursă printr-un antet Location; ștergerea răspunde cu 204 No Content — succes fără nimic de spus.
GET, POST și DELETE pe aceeași resursă
Trei verbe, trei răspunsuri: lista revine cu 200, crearea cu 201 și un antet Location, iar ștergerea cu 204 și un corp gol.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /notes", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "note: cumpărături, idei")
})
mux.HandleFunc("POST /notes", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", "/notes/3")
w.WriteHeader(http.StatusCreated)
fmt.Fprintln(w, "creat")
})
mux.HandleFunc("DELETE /notes/{id}", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
server := httptest.NewServer(mux)
defer server.Close()
client := &http.Client{}
get, _ := http.Get(server.URL + "/notes")
body, _ := io.ReadAll(get.Body)
fmt.Println("GET /notes ->", get.StatusCode, strings.TrimSpace(string(body)))
post, _ := http.Post(server.URL+"/notes", "application/json", strings.NewReader(`{"text": "sună-l pe Ben"}`))
fmt.Println("POST /notes ->", post.StatusCode, "Location:", post.Header.Get("Location"))
req, _ := http.NewRequest(http.MethodDelete, server.URL+"/notes/3", nil)
del, _ := client.Do(req)
fmt.Println("DELETE /notes/3 ->", del.StatusCode, "(fără conținut)")
}
Codul de stare potrivit
Și erorile merită precizie: o resursă lipsă înseamnă 404 Not Found (cu un corp de eroare JSON, ca să-l poată citi și programele), iar un verb greșit pe o cale cunoscută înseamnă 405 Method Not Allowed — pe care mux-ul din Go îl trimite în locul tău atunci când calea se potrivește, dar metoda nu.
200, 404 și 405
Nota existentă returnează 200 cu date, cea lipsă 404 cu o eroare JSON, iar cererea DELETE pe o rută doar pentru GET este refuzată cu 405 chiar de către router.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
)
func main() {
notes := map[string]string{"1": "listă de cumpărături"}
mux := http.NewServeMux()
mux.HandleFunc("GET /notes/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
text, ok := notes[r.PathValue("id")]
if !ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, `{"error": "nota nu a fost găsită"}`)
return
}
fmt.Fprintf(w, `{"id": %q, "text": %q}`+"\n", r.PathValue("id"), text)
})
server := httptest.NewServer(mux)
defer server.Close()
client := &http.Client{}
for _, path := range []string{"/notes/1", "/notes/99"} {
resp, _ := http.Get(server.URL + path)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println("GET ", path, "->", resp.StatusCode, strings.TrimSpace(string(body)))
}
// Verb greșit pe o cale cunoscută: routerul răspunde singur cu 405.
req, _ := http.NewRequest(http.MethodDelete, server.URL+"/notes/1", nil)
resp, _ := client.Do(req)
fmt.Println("DELETE /notes/1 ->", resp.StatusCode, http.StatusText(resp.StatusCode))
}