Web Routing in Go
Go provides several ways to handle URL routing in web applications. This guide covers both built-in routing capabilities and popular third-party routers.
Basic Routing
Standard HTTP Routing
package main
import (
"fmt"
"net/http"
)
func main() {
// Basic route handlers
http.HandleFunc("/", homeHandler)
http.HandleFunc("/about", aboutHandler)
http.HandleFunc("/users/", usersHandler)
// Start server
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the home page!")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "About us")
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Users section")
}
Pattern Matching
func userHandler(w http.ResponseWriter, r *http.Request) {
// Extract user ID from path
path := r.URL.Path
id := strings.TrimPrefix(path, "/users/")
if id == "" {
// List users
fmt.Fprintf(w, "List of users")
return
}
// Show specific user
fmt.Fprintf(w, "User: %s", id)
}
func main() {
http.HandleFunc("/users/", userHandler)
}
Advanced Routing
Custom Router
type Router struct {
routes map[string]http.HandlerFunc
}
func NewRouter() *Router {
return &Router{
routes: make(map[string]http.HandlerFunc),
}
}
func (r *Router) Handle(pattern string, handler http.HandlerFunc) {
r.routes[pattern] = handler
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
for pattern, handler := range r.routes {
if matched, _ := path.Match(pattern, req.URL.Path); matched {
handler(w, req)
return
}
}
http.NotFound(w, req)
}
Method-Based Routing
type Route struct {
Method string
Pattern string
Handler http.HandlerFunc
}
type Router struct {
routes []Route
}
func (r *Router) HandleFunc(method, pattern string, handler http.HandlerFunc) {
r.routes = append(r.routes, Route{
Method: method,
Pattern: pattern,
Handler: handler,
})
}
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
for _, route := range r.routes {
if route.Method == req.Method && route.Pattern == req.URL.Path {
route.Handler(w, req)
return
}
}
http.NotFound(w, req)
}
Using Popular Routers
Gorilla Mux
import "github.com/gorilla/mux"
func main() {
r := mux.NewRouter()
// Basic routes
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
// Route with variables
r.HandleFunc("/product/{id}", ProductHandler)
// Route with regex
r.HandleFunc("/articles/{category}/{id:[0-9]+}",
ArticleHandler)
// Method-specific routes
r.HandleFunc("/api/posts", PostsHandler).
Methods("GET")
r.HandleFunc("/api/posts", CreatePostHandler).
Methods("POST")
// Subrouter
s := r.PathPrefix("/api/v1").Subrouter()
s.HandleFunc("/users", UsersHandler)
http.ListenAndServe(":8080", r)
}
Chi Router
import "github.com/go-chi/chi/v5"
func main() {
r := chi.NewRouter()
// Middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
// Routes
r.Get("/", HomeHandler)
r.Get("/users", UsersHandler)
// RESTful routes
r.Route("/articles", func(r chi.Router) {
r.Get("/", ListArticles)
r.Post("/", CreateArticle)
r.Route("/{id}", func(r chi.Router) {
r.Get("/", GetArticle)
r.Put("/", UpdateArticle)
r.Delete("/", DeleteArticle)
})
})
http.ListenAndServe(":8080", r)
}
Best Practices
1. Route Organization
type API struct {
router *mux.Router
db *sql.DB
}
func NewAPI(db *sql.DB) *API {
api := &API{
router: mux.NewRouter(),
db: db,
}
api.routes()
return api
}
func (a *API) routes() {
// Group related routes
a.router.HandleFunc("/health", a.healthCheck)
// API routes
api := a.router.PathPrefix("/api/v1").Subrouter()
// User routes
users := api.PathPrefix("/users").Subrouter()
users.HandleFunc("", a.listUsers).Methods("GET")
users.HandleFunc("/{id}", a.getUser).Methods("GET")
// Product routes
products := api.PathPrefix("/products").Subrouter()
products.HandleFunc("", a.listProducts).Methods("GET")
products.HandleFunc("/{id}", a.getProduct).Methods("GET")
}
2. Route Parameters
func (a *API) getUser(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
// Validate ID
if !isValidID(id) {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
// Get user from database
user, err := a.db.GetUser(id)
if err != nil {
if err == sql.ErrNoRows {
http.Error(w, "User not found", http.StatusNotFound)
return
}
http.Error(w, "Server error", http.StatusInternalServerError)
return
}
// Return user
json.NewEncoder(w).Encode(user)
}
3. Middleware Integration
func (a *API) authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
// Validate token
user, err := a.validateToken(token)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Add user to context
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (a *API) routes() {
// Protected routes
protected := a.router.PathPrefix("/api").Subrouter()
protected.Use(a.authMiddleware)
protected.HandleFunc("/profile", a.getProfile)
protected.HandleFunc("/settings", a.getSettings)
}
Common Patterns
1. RESTful Routing
type ResourceHandler struct {
db *sql.DB
}
func (h *ResourceHandler) Register(r *mux.Router) {
r.HandleFunc("", h.List).Methods("GET")
r.HandleFunc("", h.Create).Methods("POST")
r.HandleFunc("/{id}", h.Get).Methods("GET")
r.HandleFunc("/{id}", h.Update).Methods("PUT")
r.HandleFunc("/{id}", h.Delete).Methods("DELETE")
}
func main() {
r := mux.NewRouter()
// Register resource handlers
users := &ResourceHandler{db: db}
r.PathPrefix("/users").Handler(users)
products := &ResourceHandler{db: db}
r.PathPrefix("/products").Handler(products)
}
2. Versioned API Routes
func NewAPIRouter() *mux.Router {
r := mux.NewRouter()
// API versions
v1 := r.PathPrefix("/api/v1").Subrouter()
registerV1Routes(v1)
v2 := r.PathPrefix("/api/v2").Subrouter()
registerV2Routes(v2)
return r
}
func registerV1Routes(r *mux.Router) {
r.HandleFunc("/users", v1.ListUsers).Methods("GET")
r.HandleFunc("/users/{id}", v1.GetUser).Methods("GET")
}
func registerV2Routes(r *mux.Router) {
r.HandleFunc("/users", v2.ListUsers).Methods("GET")
r.HandleFunc("/users/{id}", v2.GetUser).Methods("GET")
}
Next Steps
- Learn about Middleware
- Explore Sessions
- Study Authentication