Go
Summary - net/http
Combine HandleFunc and JSON to write an API.
net/http summary
Client: an http.Client with a timeout, NewRequestWithContext, Do, checking StatusCode, defer resp.Body.Close(). Server: http.Handler/HandleFunc, Go 1.22 ServeMux patterns ("GET /path/{id}"), an http.Server with timeouts, JSON helpers, MaxBytesReader. Testing: httptest.NewRecorder and httptest.NewServer.
Graceful shutdown
To avoid cutting off in-flight requests when stopping the server, use Shutdown:
go func() {
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}()
<-ctx.Done() // SIGTERM
kapat, iptal := context.WithTimeout(context.Background(), 10*time.Second)
defer iptal()
srv.Shutdown(kapat)
Shutdown refuses new connections and waits until current requests finish (or time runs out).
Exercise End to end
In a ServeMux, the "GET /saglik" route returns the JSON {"durum":"ok"}. Start it with httptest.NewServer(mux), request it with http.Get, decode the body into a map[string]string and print the durum value.
Show example solution
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /saglik", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"durum": "ok"})
})
srv := httptest.NewServer(mux)
defer srv.Close()
resp, err := http.Get(srv.URL + "/saglik")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
var m map[string]string
json.NewDecoder(resp.Body).Decode(&m)
fmt.Println(m["durum"])
}
Test yourself
-
How do you test a handler without opening a real network port?
- It isn't possible
- httptest.NewRecorder + httptest.NewRequest
- http.ListenAndServe
- curl
Show answer
httptest.NewRecorder + httptest.NewRequest — The Recorder imitates a ResponseWriter; the handler is called directly and the code, headers and body are read from memory.
-
Shutdown error
if err := srv.ListenAndServe(); err != http.____ {
log.Fatal(err)
}
Show answer
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
-
Group summary
- http.Client{Timeout}
- Safe client
- ServeMux
- Routing
- httptest
- Network-free testing
- srv.Shutdown
- Graceful shutdown
Next lesson: The flag package