Problem: You want to process HTTP requests and send back HTTP responses.
Solution: Use http.Request to extract information on HTTP requests and http.ResponseWriter to send HTTP responses back.
The http.Request struct represents an HTTP request message sent from the client. The struct contains important information about the request, as well as several useful methods. Some important parts of Request are:
• URL
• Header
• Body
• Form, PostForm, and MultipartForm
You can also get access to the cookies in the request and the referring URL, and the user agent from methods in Request .
The URL field is a representation of the URL sent as part of the request line (the first line of the HTTP request). The URL field is a pointer to the url.URL type.
The URL will look like this:
scheme://[userinfo@]host/path[?query]
The two most commonly used pieces of information from the URL of a request are the path and the query. The path is the path to the resource. The query is the query string, which often has the form of a key-value pair, for example, ?key=value .
You can also get the HTTP method as well as the hostname. The HTTP method is the method used to make the request, such as GET , POST , PUT , DELETE , and so on. The hostname is the name of the server that the request was sent to.
Here’s a quick look at this:
func main() { http.HandleFunc("/hello/world", hello) http.ListenAndServe(":8000", nil) } func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Method : %s, Host : %s", r.Method, r.Host) fmt.Fprintf(w, "Path : %s, Query : %s\n", r.URL.Path, r.URL.Query()) }
Run this on the server and open http://localhost:8000/hello/world?name=sausheong . Figure 17-2 shows a screenshot of a browser showing the method and host of the request, as well as the path and query of the URL.
As you can see, the Query field is a map of key-value pairs.
Next are the request headers. The Header field of an http.Request is a map of all the HTTP headers sent with the request. The keys in the map are the header names and the values in the map are slices of strings, so if a header appears multiple times in the request, the values will be appended to the slice:
func main() { http.HandleFunc("/headers", headers) http.ListenAndServe(":8000", nil) } func headers(w http.ResponseWriter, r *http.Request) { for k, v := range r.Header { fmt.Fprintf(w, "%s: %s\n", k, v) } }
Run this on the server and open http://localhost:8000/headers . Figure 17-3 is a screenshot of a browser showing the request headers.
Don’t be overly taken by the headers. These are simply the headers that are sent by the browser. In this case, I’m using Safari, so these are the headers sent by Safari. If you use a different browser, you will see different headers.
Next is the body of a request. The Body field of an http.Request is an io.ReadCloser that contains the request body. The request body is available only if the request has a body, such as a POST request:
func main() { http.HandleFunc("/body", body) http.ListenAndServe(":8000", nil) } func body(w http.ResponseWriter, r *http.Request) { body, _ := io.ReadAll(r.Body) fmt.Fprintf(w, "%s", body) }
Since the Body field is an io.ReadCloser , you can use the io.ReadAll function to read the entire body into a byte slice. To test this, you can use the curl command:
$ curl -X POST -d "Hello World" http://localhost:8000/body
The curl command will send a POST request with the body Hello World to the server. The server will then send back the body of the request. If you run this on the command line, you will see the following:
% curl -X POST -d "Hello World" http://localhost:8000/body Hello World