DEV Community

Gaurav Saini
Gaurav Saini

Posted on

2

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!

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Generate and update README files, create data-flow diagrams, and keep your project fully documented. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE