Go Routing Tutorial
Routing in Go handles HTTP requests by matching URLs to handler functions. This tutorial covers both the standard library and popular third-party routers.
1. Basic Routing
1.1 Standard Library Router
The net/http package provides basic routing capabilities:
package main
import (
"fmt"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Home Page")
}
func main() {
// Simple route handling
http.HandleFunc("/", homeHandler)
http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "About Page")
})
// Start server
http.ListenAndServe(":8080", nil)
}
Key Points:
- http.HandleFunc: Binds path to handler function
- Exact matching: "/about" won't match "/about/"
- No path parameters: Basic routing only
2. Advanced Routing
2.1 Using gorilla/mux
The gorilla/mux router adds powerful routing features:
package main
import (
"github.com/gorilla/mux"
"net/http"
)
func main() {
r := mux.NewRouter()
// Exact path matching
r.HandleFunc("/products/{id}", productHandler).Methods("GET")
// Route with regex validation
r.HandleFunc("/articles/{category:[a-z]+}", articlesHandler)
// Subrouter with prefix
s := r.PathPrefix("/admin").Subrouter()
s.HandleFunc("/dashboard", adminDashboard)
http.ListenAndServe(":8080", r)
}
gorilla/mux Features:
- Path parameters: Capture values from URLs
- Method matching: Restrict to GET/POST/etc
- Regex validation: Validate path segments
- Subrouters: Group related routes
3. RESTful Routing
3.1 Resource-Based Routes
Proper REST APIs use HTTP methods for CRUD operations:
func main() {
r := mux.NewRouter()
// Collection routes
r.HandleFunc("/users", listUsers).Methods("GET")
r.HandleFunc("/users", createUser).Methods("POST")
// Item routes
r.HandleFunc("/users/{id}", getUser).Methods("GET")
r.HandleFunc("/users/{id}", updateUser).Methods("PUT")
r.HandleFunc("/users/{id}", deleteUser).Methods("DELETE")
// Nested resources
r.HandleFunc("/users/{id}/posts", getUserPosts).Methods("GET")
}
3.2 Route Naming
Name routes for URL generation:
r.HandleFunc("/users/{id}", getUser).
Methods("GET").
Name("user")
// Generate URL later
url, _ := r.Get("user").URL("id", "123")
fmt.Println(url.Path) // "/users/123"
4. Middleware in Routing
4.1 Route-Specific Middleware
Apply middleware to specific routes or groups:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
func main() {
r := mux.NewRouter()
// Global middleware
r.Use(loggingMiddleware)
// Route-specific middleware
r.Handle("/admin", adminHandler).Middleware(authMiddleware)
// Subrouter with middleware
api := r.PathPrefix("/api").Subrouter()
api.Use(jsonMiddleware)
}
4.2 Common Middleware Examples
// CORS middleware
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
next.ServeHTTP(w, r)
})
}
// Authentication middleware
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !isAuthenticated(r) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
5. Performance Considerations
5.1 Router Benchmarks
Different routers have varying performance characteristics:
// Standard library: Fastest but least features
http.HandleFunc("/", handler)
// gorilla/mux: Feature-rich with moderate overhead
r := mux.NewRouter()
r.HandleFunc("/", handler)
// httprouter: High performance with good features
router := httprouter.New()
router.GET("/", handler)
// chi: Lightweight with good performance
r := chi.NewRouter()
r.Get("/", handler)
5.2 Optimization Tips
- Compile routes once at startup
- Avoid regex in hot paths when possible
- Use simple patterns for static routes
- Benchmark with your specific workload
6. Common Pitfalls
6.1 Trailing Slash Mismatch
Routes with and without trailing slashes are different:
r.HandleFunc("/path", handler1) // Doesn't match "/path/"
r.HandleFunc("/path/", handler2) // Doesn't match "/path"
// Solution: Handle both explicitly or use StrictSlash(true)
r := mux.NewRouter()
r.StrictSlash(true)
r.HandleFunc("/path", handler) // Now matches both
6.2 Route Conflicts
Ambiguous routes can cause unexpected matches:
// Problematic ordering
r.HandleFunc("/users/{id}", userHandler)
r.HandleFunc("/users/new", newUserHandler) // Never matches
// Solution: Specific routes first
r.HandleFunc("/users/new", newUserHandler)
r.HandleFunc("/users/{id}", userHandler)
×