Gaurav Saini
Posted on April 21, 2024
Hello 👋
I've started learning Go a couple days ago. Currently, I'm building Rest APIs using net/http
package.
I have figured out most of the basic stuff, but struggling a bit with subrouting. When I add a subrouter using like this
var mux1 = http.NewServeMux()
mux1.HandleFunc(...)
var mux2 = http.NewServeMux()
mux2.HandleFunc(...)
mux1.Handle("/", middleware.IsAuthenticated(mux2))
I'm trying to add a protected set of routes to a router using a middleware. But, the routes defined in the subrouter are not accessible without adding a trailing slash in the URL when making the request. eg:
My Code
// cmd/main/main.go
package main
import (
"fmt"
"net/http"
<span class="s">"rest-api/internal/middleware"</span>
<span class="s">"rest-api/internal/routes"</span>
<span class="s">"rest-api/internal/utils"</span>
)
func main() {
var middlewareStack = utils.CreateStack(middleware.LogRequest)
<span class="k">var</span> <span class="n">v1Router</span> <span class="o">=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewServeMux</span><span class="p">()</span>
<span class="n">v1Router</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="s">"/v1/"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StripPrefix</span><span class="p">(</span><span class="s">"/v1"</span><span class="p">,</span> <span class="n">routes</span><span class="o">.</span><span class="n">EchoRouter</span><span class="p">()))</span>
<span class="k">var</span> <span class="n">usersRouter</span> <span class="o">=</span> <span class="n">routes</span><span class="o">.</span><span class="n">UsersRouter</span><span class="p">()</span>
<span class="n">v1Router</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="s">"/users/"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StripPrefix</span><span class="p">(</span><span class="s">"/users"</span><span class="p">,</span> <span class="n">usersRouter</span><span class="p">))</span>
<span class="k">var</span> <span class="n">server</span> <span class="o">=</span> <span class="o">&</span><span class="n">http</span><span class="o">.</span><span class="n">Server</span><span class="p">{</span>
<span class="n">Addr</span><span class="o">:</span> <span class="s">":8080"</span><span class="p">,</span>
<span class="n">Handler</span><span class="o">:</span> <span class="n">middlewareStack</span><span class="p">(</span><span class="n">v1Router</span><span class="p">),</span>
<span class="p">}</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="s">"starting server on port 8080"</span><span class="p">)</span>
<span class="n">server</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">()</span>
}
// internal/routes/users.go
package routes
import (
"encoding/json"
"net/http"
<span class="s">"rest-api/internal/middleware"</span>
)
type User struct {
Name string
Email string
}
func UsersRouter() http.Handler {
var usersMux = http.NewServeMux()
usersMux.HandleFunc("GET /", getUsers)
<span class="k">var</span> <span class="n">protectedMux</span> <span class="o">=</span> <span class="n">http</span><span class="o">.</span><span class="n">NewServeMux</span><span class="p">()</span>
<span class="n">protectedMux</span><span class="o">.</span><span class="n">HandleFunc</span><span class="p">(</span><span class="s">"POST /"</span><span class="p">,</span> <span class="n">createUser</span><span class="p">)</span>
<span class="n">usersMux</span><span class="o">.</span><span class="n">Handle</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span> <span class="n">middleware</span><span class="o">.</span><span class="n">IsAuthenticated</span><span class="p">(</span><span class="n">protectedMux</span><span class="p">))</span>
<span class="k">return</span> <span class="n">usersMux</span>
}
func getUsers(res http.ResponseWriter, _ *http.Request) {
res.Write([]byte("users list"))
}
func createUser(res http.ResponseWriter, req *http.Request) {
var newUser User
var err = json.NewDecoder(req.Body).Decode(&newUser)
<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="n">res</span><span class="o">.</span><span class="n">WriteHeader</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusBadRequest</span><span class="p">)</span>
<span class="n">res</span><span class="o">.</span><span class="n">Write</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">()))</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">res</span><span class="o">.</span><span class="n">WriteHeader</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusCreated</span><span class="p">)</span>
<span class="n">res</span><span class="o">.</span><span class="n">Write</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"new user created, name: "</span> <span class="o">+</span> <span class="n">newUser</span><span class="o">.</span><span class="n">Name</span> <span class="o">+</span> <span class="s">", email: "</span> <span class="o">+</span> <span class="n">newUser</span><span class="o">.</span><span class="n">Email</span><span class="p">))</span>
}
PS
I can achieve the desired behaviour by using the /users
prefix in the users.go
file instead of the main.go
file, but I was wondering if it can be done this way too 🤔
Thanks!
Posted on April 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.