5.4. HTTP Server
Quick start
Go allows us to start a simple HTTP server with minimal code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| package main
import (
"fmt"
"net/http"
"os"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
}
func main() {
http.HandleFunc("/hello", helloHandler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
|
Now you can access http://localhost:8080/hello
.
http.Handler
The http.Handler
interface is the basic building block of all HTTP servers.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
The ServeHTTP method has two parameters:
- http.ResponseWriter
is an interface with the following methods:
Header() returns all headers and allows setting new ones.WriteHeader(statusCode int) allows setting a status code for the response. It must be called before Write() and defaults to http.StatusOK if not set. For a full list of status codes see the documentation
.Write() allows us to use all functions that expect an io.Writer
interface (e.g. fmt.Fprint
or io.WriteString
)
- http.Request
defines the request parameters. Example:
- Method: GET, POST, PUT etc.
- Header: request headers
- Body: request body (typically used with POST requests)
- Form: form values
So a minimal handler would look like this:
type myHandler struct{}
func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
With the function http.HandlerFunc
we can transform a function with the following signature into a http.Handler.
func myHandlerFunc(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello")
}
handler := http.HandlerFunc(myHandlerFunc)
http.ResponseWriter
The following is an example how to use http.ResponseWriter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| package main
import (
"fmt"
"net/http"
"os"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
// set content type header
w.Header().Set("Content-Type", "text/plain")
// set status header (400)
w.WriteHeader(http.StatusBadRequest)
// respond with an error message
// `fmt.Fprint` expects an `io.Writer` interface
fmt.Fprint(w, "invalid request")
}
func main() {
http.HandleFunc("/hello", helloHandler)
// ListenAndServe always returns a non-nil error.
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
|
http.Request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| package main
import (
"fmt"
"net/http"
"os"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Method: ", r.Method)
fmt.Fprintln(w, "URL: ", r.URL)
fmt.Fprintln(w, "Proto: ", r.Proto)
fmt.Fprintln(w, "Header: ", r.Header)
fmt.Fprintln(w, "Body: ", r.Body)
fmt.Fprintln(w, "ContentLength: ", r.ContentLength)
fmt.Fprintln(w, "Form: ", r.Form)
fmt.Fprintln(w, "RemoteAddr: ", r.RemoteAddr)
}
func main() {
http.HandleFunc("/hello", helloHandler)
// ListenAndServe always returns a non-nil error.
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
|
Receive Data
The field Body in the http.Request
implements io.Reader
. We can use functions that expect an io.Reader like io.ReadAll
to read the body.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| package main
import (
"io"
"log"
"net/http"
)
func echoHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
w.Write(body)
}
func main() {
http.HandleFunc("/echo", echoHandler)
// ListenAndServe always returns a non-nil error.
log.Fatal(http.ListenAndServe(":8080", nil))
}
|
Try sending a request with curl:
curl -d 'body' http://localhost:8080/hello
Middeware
A common pattern is to wrap one handler within another handler. This could for example be used for logging or authentication. For this we pass our actual handler (inner handler) to the middleware.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| package main
import (
"fmt"
"log"
"net/http"
)
func secretHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "top secret")
}
}
func authMiddleware(next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, password, _ := r.BasicAuth()
if !(user == "admin" && password == "secret") {
code := http.StatusUnauthorized
http.Error(w, http.StatusText(code), code)
return
}
next.ServeHTTP(w, r)
}
}
func main() {
http.Handle("/secret", authMiddleware(secretHandler()))
log.Fatal(http.ListenAndServe(":8080", nil))
}
|