HTTP/HTTPS server with Go 1.22, with nested routing, middleware and more
Intro
In my effort to learn Go 1.22 I've tried to see which of my skills learned in express/node.js can be transferred to GO.
This project includes a TLS HTTPS server option or a basic HTTP server option, using environment variables. See details in the README.md https://github.com/GeorgeCrisan/go-web-server/blob/master/README.md file.
The full project source code can be found in this public GIT repository if you want to get your hands dirty. https://github.com/GeorgeCrisan/go-web-server
The server is set with the latest version of Go which allows you to define the CRUD verbs on the endpoint.
// Set the route functionality
router.HandleFunc("GET /user/{userId}/token/{token}", userHandler)
You can see how to set nested or versioned routing.
// Allows to point certain routes to other routers
proxyRouter := http.NewServeMux()
proxyRouter.Handle("/api/v1/", http.StripPrefix("/api/v1", router))
Middleware functions
The example below shows how to set middleware functions and how to chain them to prevent ugly function invocation nesting.
// Logger middleware
func RequestLogger(next http.Handler) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
log.Printf("Log info method: %s, path : %s, info: %s ", req.Method, req.URL.Path, req.UserAgent())
next.ServeHTTP(res, req)
}
}
// Mock dumb validator as middleware proof of concept
func RequestValidator(next http.Handler) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
token := "1234"
if !strings.Contains(req.URL.Path, token) {
log.Printf("Token is required !!!")
http.Error(res, "Unauthorised so fuck off", http.StatusUnauthorized)
// Early return
return
}
log.Printf("Security Token found, move on")
next.ServeHTTP(res, req)
}
}
This is an example of a middleware chaining function.
// Types
// type Middleware func(next http.Handler) http.HandlerFunc
type Middleware func(http.Handler) http.HandlerFunc
// Chain middleware util function
func ChainMiddleware(middlwareList ...Middleware) Middleware {
return func(next http.Handler) http.HandlerFunc {
// iterate from all middleware, invoke each middleware and pass object to next middleware
for i := len(middlwareList) - 1; i >= 0; i-- {
next = middlwareList[i](next)
}
return next.ServeHTTP
}
}
and this is how to glue the middleware together to the route handler.
// Define the middleware we want to use for this router
midlewareChained := ChainMiddleware(
RequestLogger,
RequestValidator,
)
// Set the route functionality
router.HandleFunc("GET /user/{userId}/token/{token}", userHandler)
// Chain middleware to the router
server := http.Server{
Addr: httpServer.address,
Handler: midlewareChained(router),
}
This was a very interesting experience for me. Go promotes simplicity and clean code and I am loving it. If you want to discuss this project or if you have any questions email me at georgerdp@gmail.com or find me on linked in https://www.linkedin.com/in/george-crisan26/.