GO RESTFUL — #6
Comparing Golang’s built-in HTTP server implementation with the most popular community package gorilla/mux
This article is a summary of my experiments with Golang HTTP using both the builtin functionality and the most popular community-developed package to figure out an effective way to declare HTTP views and handle HTTP requests.
Builtin net/http package
Declare handlers
The net/http
package contains all utilities needed to accept requests and handle them dynamically. We can register handlers with http.HandleFunc
. The handler’s first parameter takes a string path to match, and the second parameter is a function to execute when that path is matched.
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Go RESTful Series")
}
The matching algorithm is a prefix match, so the above declaration will route every request to the inline anonymous function. That is, since it’s only /
, all routes will match it.
Read request
Every handler function receives a *http.Request
as the second parameter, which is a struct instance pointer containing all information related to the request.
r.Method // request method
r.URL // request URL
r.Header // request headers
r.Body // request body
Refer here for full http.Request
API reference.
Write the response
Every handler function receives a http.ResponseWriter
as the first parameter, which exposes APIs for writing the response’s headers and body. It implements io.Writer
interface so that we can write the response similar to writing to files/stdout, etc.
// Set header
w.Header().Set("Some-Header", "value")// Write using standard write method in fmt package.
fmt.Fprintf(w, "Body content")// Write body will automatically write all previous-set header fields.
Start the server
In order to run the above code, we need to start the server.
http.ListenAndServe(":80", nil)
This will start the default HTTP server with default routes that we set using http.HandleFunc
. If you want to create a new server and manage it in your own, please refer here, for the sake of this article, the default server is sufficient.
Community-developed packages
Go’s net/http
package has already provided a decent number of APIs for writing HTTP applications. However, there’s one thing that it doesn’t do very well which is complex request routing like segmenting a request URL into single parameters. Fortunately, there’s a very popular community-developed package that handles that responsibilities named gorilla/mux
.
Import
import "github.com/gorilla/mux"
Refactoring the code to use gorilla/mux
To apply gorilla/mux
, we need to make some updates to the previous implementation.
// Create a new mux router.
r := mux.NewRouter()// Replace http.HandleFunc by router.HandleFunc.
r.HandleFunc("/", handler)// Replace 2nd parameter by the configured mux router.
http.ListenAndServe(":80", r)
Declare complex routes
With gorilla/mux
, we can declare complex routes with variables, constrain routes with methods, etc.
r.HandleFunc("/users/", listUsers).Methods(http.MethodGet)
r.HandleFunc("/users/", createUser).Methods(http.MethodPost)
r.HandleFunc("/users/{userId}/", getUser).Methods(http.MethodGet)
r.HandleFunc("/users/{userId}/", updateUser).Methods(http.MethodPut)
r.HandleFunc("/users/{userId}/", deleteUser).Methods(Http.MethodDelete)
Refer here for full gorilla/mux
API reference.
Get captured variables
We declared routes with variables, we should be able to capture the values of those variables in handlers.
func handler(w http.ResponseWriter, r *http.Request) {
// mux.Vars(r) returns all values captured in the request URL.
vars := mux.Vars(r) // vars is a dictionary whose key-value pairs are variables' name-value pairs.
fmt.Fprintf(w, "User %s\n", vars["userId"])
}
As you can see, the standard HTTP server implementation in Go is already powerful. By using gorilla/mux
we are able to get an even simpler interface for dealing with complex routing and managing requests.