In Go language web development, choosing the right routing library is crucial. Chi and HttpRouter are both lightweight routing solutions favored by developers, each with its own characteristics suitable for different scenarios.Among them, Chi is a feature-rich modular router built on the standard library net/http, known for its modular design and strong middleware support. It is fully compatible with net/http, offers a rich middleware ecosystem, supports modular routing organization, naturally supports REST API design, and has built-in request context management.Here is an example of using it:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
)
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
}
var products = []Product{
{ID: 1, Name: "Laptop", Price: 5999.99},
{ID: 2, Name: "Smartphone", Price: 3999.50},
}
func main() {
r := chi.NewRouter()
// Use middleware
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(60 * time.Second))
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
}))
// API version routing group
r.Route("/api/v1", func(r chi.Router) {
// Product routing group
r.Route("/products", func(r chi.Router) {
r.Get("/", getProducts)
r.Post("/", createProduct)
r.Route("/{id}", func(r chi.Router) {
r.Get("/", getProduct)
r.Put("/", updateProduct)
r.Delete("/", deleteProduct)
})
})
// Health check
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
})
fmt.Println("Server running on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
func getProducts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(products)
}
func getProduct(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
for _, p := range products {
if p.ID == id {
json.NewEncoder(w).Encode(p)
return
}
}
http.Error(w, "Product not found", http.StatusNotFound)
}
func createProduct(w http.ResponseWriter, r *http.Request) {
var product Product
if err := json.NewDecoder(r.Body).Decode(&product); err != nil {
http.Error(w, "Invalid data", http.StatusBadRequest)
return
}
product.ID = len(products) + 1
products = append(products, product)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(product)
}
func updateProduct(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
var updated Product
if err := json.NewDecoder(r.Body).Decode(&updated); err != nil {
http.Error(w, "Invalid data", http.StatusBadRequest)
return
}
for i, p := range products {
if p.ID == id {
updated.ID = id
products[i] = updated
json.NewEncoder(w).Encode(updated)
return
}
}
http.Error(w, "Product not found", http.StatusNotFound)
}
func deleteProduct(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
for i, p := range products {
if p.ID == id {
products = append(products[:i], products[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
http.Error(w, "Product not found", http.StatusNotFound)
}
Github link: github.com/go-chi/chiAnother HttpRouter is an extremely performant lightweight router. It is known for its excellent performance and minimal memory usage, adopted by many high-performance frameworks like Gin. It features a highly optimized routing matching algorithm, with almost no memory allocation during routing matching, automatically handles duplicate slashes and path normalization, and has a clean and easy-to-use interface design with type-safe routing parameters.Here is an example of using it:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"github.com/julienschmidt/httprouter"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var users = []User{
{ID: 1, Name: "Zhang San", Email: "[email protected]"},
{ID: 2, Name: "Li Si", Email: "[email protected]"},
}
func main() {
router := httprouter.New()
// Set custom 404 handler
router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Page not found", http.StatusNotFound)
})
// Set method not allowed handler
router.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
})
// Define routes
router.GET("/api/users", GetUsers)
router.POST("/api/users", CreateUser)
router.GET("/api/users/:id", GetUser)
router.PUT("/api/users/:id", UpdateUser)
router.DELETE("/api/users/:id", DeleteUser)
// Static file service
router.ServeFiles("/static/*filepath", http.Dir("./static"))
fmt.Println("Server running on :8081")
log.Fatal(http.ListenAndServe(":8081", router))
}
func GetUsers(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
func GetUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
id, err := strconv.Atoi(ps.ByName("id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
for _, user := range users {
if user.ID == id {
json.NewEncoder(w).Encode(user)
return
}
}
http.Error(w, "User not found", http.StatusNotFound)
}
func CreateUser(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "Invalid data", http.StatusBadRequest)
return
}
user.ID = len(users) + 1
users = append(users, user)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
func UpdateUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
id, err := strconv.Atoi(ps.ByName("id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
var updated User
if err := json.NewDecoder(r.Body).Decode(&updated); err != nil {
http.Error(w, "Invalid data", http.StatusBadRequest)
return
}
for i, user := range users {
if user.ID == id {
updated.ID = id
users[i] = updated
json.NewEncoder(w).Encode(updated)
return
}
}
http.Error(w, "User not found", http.StatusNotFound)
}
func DeleteUser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
id, err := strconv.Atoi(ps.ByName("id"))
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
for i, user := range users {
if user.ID == id {
users = append(users[:i], users[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
http.Error(w, "User not found", http.StatusNotFound)
}
Github link: github.com/julienschmidt/httprouterBoth of these are excellent Go routing libraries, and the choice between them depends on your specific needs: Chi is more suitable for complex projects that require rich features, middleware support, and modular design, while HttpRouter is better for applications that demand extreme performance and simplicity.
Finally, I would like to recommend my mini program:
