DEV Community

Gaurav Saini
Gaurav Saini

Posted on

Help needed with subrouting in Go

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))


Enter fullscreen mode Exit fullscreen mode

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:

Screenshot of curl outputs with and without trailing slash

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>
Enter fullscreen mode Exit fullscreen mode

)

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">&amp;</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>
Enter fullscreen mode Exit fullscreen mode

}

// internal/routes/users.go
package routes

import (
"encoding/json"
"net/http"

<span class="s">"rest-api/internal/middleware"</span>
Enter fullscreen mode Exit fullscreen mode

)

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>
Enter fullscreen mode Exit fullscreen mode

}

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>
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode




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!

Top comments (0)