Go HTTP Server
An HTTP server is a program that receives requests and sends back responses. A handler is the function that writes one response.
A handler answers through a recorder
ResponseWriter is the value a handler writes its response into. A recorder is a ResponseWriter that saves the response instead of sending it.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello from the handler")
}
func main() {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
hello(rec, req)
fmt.Println("Status:", rec.Code)
fmt.Println(rec.Body.String())
}A mux routes two paths
A ServeMux is a router that matches a request path to a handler. HandleFunc registers a handler for one path on the mux.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello there")
}
func bye(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Goodbye for now")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", hello)
mux.HandleFunc("/bye", bye)
rec1 := httptest.NewRecorder()
req1 := httptest.NewRequest("GET", "/hello", nil)
mux.ServeHTTP(rec1, req1)
fmt.Println("Status:", rec1.Code)
fmt.Println(rec1.Body.String())
rec2 := httptest.NewRecorder()
req2 := httptest.NewRequest("GET", "/bye", nil)
mux.ServeHTTP(rec2, req2)
fmt.Println("Status:", rec2.Code)
fmt.Println(rec2.Body.String())
}A handler reads a query value
A query value is extra data placed in an address after a question mark. The handler reads the name value from the query and greets that name.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func greet(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Fprintf(w, "Hello, %s", name)
}
func main() {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/greet?name=Hana", nil)
greet(rec, req)
fmt.Println("Status:", rec.Code)
fmt.Println(rec.Body.String())
}A handler answers with JSON and a header
A header is extra information sent with a response. This handler sets the content type header first and then writes a JSON body.
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
)
type Book struct {
Title string `json:"title"`
}
func book(w http.ResponseWriter, r *http.Request) {
data, err := json.Marshal(Book{Title: "Go Basics"})
if err != nil {
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(data))
}
func main() {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/", nil)
book(rec, req)
fmt.Println("Status:", rec.Code)
fmt.Println("Content-Type:", rec.Header().Get("Content-Type"))
fmt.Println(rec.Body.String())
}Example
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func greet(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Fprintf(w, "Hello, %s", name)
}
func farewell(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Fprintf(w, "Goodbye, %s", name)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/greet", greet)
mux.HandleFunc("/farewell", farewell)
rec1 := httptest.NewRecorder()
req1 := httptest.NewRequest("GET", "/greet?name=Hana", nil)
mux.ServeHTTP(rec1, req1)
fmt.Println("Status:", rec1.Code)
fmt.Println(rec1.Body.String())
rec2 := httptest.NewRecorder()
req2 := httptest.NewRequest("GET", "/farewell?name=Hana", nil)
mux.ServeHTTP(rec2, req2)
fmt.Println("Status:", rec2.Code)
fmt.Println(rec2.Body.String())
}Key points
- A handler writes its response into the ResponseWriter it receives.
- httptest.NewRecorder records a response so a program can inspect it.
- The recorder Code field holds the status number of the response.
- A ServeMux sends each request path to the handler registered for it.
- Use HandleFunc to register a handler for a path on a mux.
- Use http.ListenAndServe to run a mux on a real address for real requests.
In this example, a new mux registers a greet handler and a farewell handler. The first request carries the name Hana in its query. The mux sends that request to the greet handler, and the recorder saves the reply. The second request goes to the farewell path with the same name. The printed lines show both replies with status 200.
Note
A recorder never opens a port, so recorder calls stay inside the program.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท Go Playground
Exercise
Which value lets a program inspect a handler response without a network call?