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!

Image of Docusign

Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

šŸ‘‹ Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay