Un API mic, dar complet
E momentul să punem piesele cap la cap: un API pentru utilizatori cu cele cinci rute clasice —
POST /users creează · GET /users listează · GET /users/{id} citește unul · PUT /users/{id} înlocuiește · DELETE /users/{id} șterge.
Depozitul este o hartă ținută în memorie, așa că exemplul rămâne de sine stătător. (Un server real tratează cererile în paralel, deci o versiune de producție ar proteja harta cu un lacăt și probabil ar înlocui-o cu o bază de date — forma funcțiilor de tratare nu s-ar schimba.)
Creare și listare
POST decodifică JSON-ul primit, îi dă utilizatorului un id, îl salvează și răspunde cu 201 și înregistrarea creată — id-ul inclus, ca să îl afle și clientul. GET întoarce toți utilizatorii ca tablou JSON.
POST /users și GET /users
Două creări, apoi lista: fiecare POST răspunde cu 201 și utilizatorul salvat, iar GET-ul final îi arată pe amândoi — starea API-ului a crescut cu fiecare cerere.
Ieșire: program Go și rezultatul capturat
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
users := map[int]User{}
nextID := 1
mux := http.NewServeMux()
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, "JSON invalid", http.StatusBadRequest)
return
}
u.ID = nextID
nextID++
users[u.ID] = u
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(u)
})
mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {
list := []User{}
for id := 1; id < nextID; id++ {
if u, ok := users[id]; ok {
list = append(list, u)
}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(list)
})
server := httptest.NewServer(mux)
defer server.Close()
for _, name := range []string{"Ana", "Ben"} {
resp, _ := http.Post(server.URL+"/users", "application/json",
strings.NewReader(`{"name": "`+name+`"}`))
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println("POST /users ->", resp.StatusCode, strings.TrimSpace(string(body)))
}
resp, _ := http.Get(server.URL + "/users")
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println("GET /users ->", resp.StatusCode, strings.TrimSpace(string(body)))
}
Citirea unuia, înlocuirea, ștergerea
Rutele cu {id} completează setul: citirea răspunde cu 200 sau 404, înlocuirea decodifică datele noi peste id-ul existent, iar ștergerea răspunde cu 204. După ștergere, același GET care mergea înainte întoarce cinstit 404 — resursa nu mai există.
GET, PUT și DELETE pe /users/{id}
Viața completă a unei resurse: o citim, o înlocuim (răspunsul poartă numele nou), o ștergem și nu mai reușim să o citim — fiecare pas cu codul de status corect.
Ieșire: program Go și rezultatul capturat
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strconv"
"strings"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
users := map[int]User{1: {ID: 1, Name: "Ana"}}
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(r.PathValue("id"))
w.Header().Set("Content-Type", "application/json")
u, ok := users[id]
if !ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, `{"error": "utilizator negăsit"}`)
return
}
json.NewEncoder(w).Encode(u)
})
mux.HandleFunc("PUT /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(r.PathValue("id"))
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, "JSON invalid", http.StatusBadRequest)
return
}
u.ID = id
users[id] = u
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(u)
})
mux.HandleFunc("DELETE /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(r.PathValue("id"))
delete(users, id)
w.WriteHeader(http.StatusNoContent)
})
server := httptest.NewServer(mux)
defer server.Close()
client := &http.Client{}
do := func(method, path, payload string) {
var reqBody io.Reader
if payload != "" {
reqBody = strings.NewReader(payload)
}
req, _ := http.NewRequest(method, server.URL+path, reqBody)
resp, _ := client.Do(req)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("%-6s %s -> %d %s\n", method, path, resp.StatusCode,
strings.TrimSpace(string(body)))
}
do("GET", "/users/1", "")
do("PUT", "/users/1", `{"name": "Ana Maria"}`)
do("DELETE", "/users/1", "")
do("GET", "/users/1", "")
}