Routing System
Overview
Weber's routing system maps HTTP requests to handler functions. It supports various HTTP methods, URL parameters, and middleware integration.
Basic Routing
Defining Routes
import "weber/backend/app"
func SetupRoutes(a *app.App) {
// GET request
a.GET("/", IndexHandler)
// POST request
a.POST("/submit", SubmitHandler)
// Multiple methods
a.Handle("/contact", ContactHandler, "GET", "POST")
}
Route Handlers
func IndexHandler(ctx *app.Context) {
ctx.Render("index.html", map[string]interface{}{
"title": "Home Page",
})
}
func SubmitHandler(ctx *app.Context) {
// Process form data
data := ctx.PostForm()
// Return JSON response
ctx.JSON(200, map[string]interface{}{
"success": true,
"message": "Data submitted successfully",
})
}
URL Parameters
Dynamic Routes
// Single parameter
a.GET("/news/:id", NewsDetailHandler)
// Multiple parameters
a.GET("/category/:category/post/:id", PostHandler)
// Optional segments
a.GET("/search/*query", SearchHandler)
Accessing Parameters
func NewsDetailHandler(ctx *app.Context) {
// Get URL parameter
id := ctx.Param("id")
// Fetch news article
article, err := model.GetNewsById(id)
if err != nil {
ctx.NotFound()
return
}
ctx.Render("news-detail.html", map[string]interface{}{
"article": article,
})
}
func PostHandler(ctx *app.Context) {
category := ctx.Param("category")
id := ctx.Param("id")
// Use parameters...
}
Query Parameters
Reading Query Strings
// URL: /search?q=golang&page=2&sort=date
func SearchHandler(ctx *app.Context) {
// Get single query parameter
query := ctx.Query("q")
// Get with default value
page := ctx.QueryDefault("page", "1")
// Get all values for a parameter
tags := ctx.QueryArray("tag")
// Convert to int
pageNum, _ := strconv.Atoi(page)
// Use parameters...
}
Request Data
Form Data
func FormHandler(ctx *app.Context) {
// Get form value
name := ctx.PostForm("name")
email := ctx.PostForm("email")
// Get all form data
form := ctx.PostFormAll()
// File upload
file, header, err := ctx.FormFile("avatar")
if err == nil {
defer file.Close()
// Process file...
}
}
JSON Body
type RequestData struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func APIHandler(ctx *app.Context) {
var data RequestData
// Bind JSON body to struct
if err := ctx.BindJSON(&data); err != nil {
ctx.Error(400, "Invalid JSON")
return
}
// Use data...
ctx.JSON(200, map[string]interface{}{
"received": data,
})
}
Route Groups
Organizing Routes
func SetupRoutes(a *app.App) {
// Public routes
a.GET("/", IndexHandler)
a.GET("/about", AboutHandler)
// News routes
newsGroup := a.Group("/news")
{
newsGroup.GET("", NewsListHandler)
newsGroup.GET("/:id", NewsDetailHandler)
newsGroup.GET("/category/:cat", NewsCategoryHandler)
}
// API routes
apiGroup := a.Group("/api")
{
apiGroup.GET("/news", APINewsHandler)
apiGroup.GET("/football", APIFootballHandler)
apiGroup.POST("/submit", APISubmitHandler)
}
// Admin routes (with auth middleware)
adminGroup := a.Group("/admin", AuthMiddleware)
{
adminGroup.GET("/dashboard", AdminDashboardHandler)
adminGroup.POST("/publish", AdminPublishHandler)
}
}
Middleware
Global Middleware
func SetupMiddleware(a *app.App) {
// Apply to all routes
a.Use(LoggerMiddleware)
a.Use(RecoveryMiddleware)
a.Use(CORSMiddleware)
}
Creating Middleware
func LoggerMiddleware(next app.HandlerFunc) app.HandlerFunc {
return func(ctx *app.Context) {
start := time.Now()
path := ctx.Path()
method := ctx.Method()
// Process request
next(ctx)
// Log after request
duration := time.Since(start)
log.Printf("%s %s - %v", method, path, duration)
}
}
func AuthMiddleware(next app.HandlerFunc) app.HandlerFunc {
return func(ctx *app.Context) {
// Check authentication
token := ctx.GetHeader("Authorization")
if !isValidToken(token) {
ctx.Error(401, "Unauthorized")
return
}
// Continue to next handler
next(ctx)
}
}
Route-Specific Middleware
// Apply to specific routes
a.GET("/protected", ProtectedHandler, AuthMiddleware, RateLimitMiddleware)
// Apply to route group
adminGroup := a.Group("/admin", AuthMiddleware, AdminCheckMiddleware)
Response Types
HTML Response
func Handler(ctx *app.Context) {
ctx.Render("template.html", data)
}
JSON Response
func APIHandler(ctx *app.Context) {
ctx.JSON(200, map[string]interface{}{
"status": "success",
"data": data,
})
}
Plain Text
func TextHandler(ctx *app.Context) {
ctx.Text(200, "Hello, World!")
}
Redirect
func RedirectHandler(ctx *app.Context) {
ctx.Redirect(302, "/new-location")
}
File Download
func DownloadHandler(ctx *app.Context) {
ctx.File("/path/to/file.pdf")
// or
ctx.FileAttachment("/path/to/file.pdf", "download.pdf")
}
Custom Response
func CustomHandler(ctx *app.Context) {
ctx.SetHeader("Content-Type", "application/xml")
ctx.SetStatus(200)
ctx.Write([]byte("<xml>...</xml>"))
}
Error Handling
Built-in Error Responses
func Handler(ctx *app.Context) {
// 404 Not Found
ctx.NotFound()
// 400 Bad Request
ctx.BadRequest("Invalid input")
// 500 Internal Server Error
ctx.InternalServerError("Something went wrong")
// Custom error
ctx.Error(403, "Forbidden")
}
Custom Error Pages
func Setup404Handler(a *app.App) {
a.NoRoute(func(ctx *app.Context) {
ctx.Render("404.html", map[string]interface{}{
"path": ctx.Path(),
})
})
}
func Setup500Handler(a *app.App) {
a.OnError(func(ctx *app.Context, err error) {
log.Error("Server error:", err)
ctx.Render("500.html", map[string]interface{}{
"message": "Internal Server Error",
})
})
}
Static Files
Serving Static Assets
func SetupStatic(a *app.App) {
// Serve directory
a.Static("/static", "./webroot/static")
// Serve single file
a.StaticFile("/favicon.ico", "./webroot/favicon.ico")
// Serve with custom handler
a.GET("/assets/*filepath", func(ctx *app.Context) {
filepath := ctx.Param("filepath")
ctx.File("./assets/" + filepath)
})
}
Request Context
Context Methods
func Handler(ctx *app.Context) {
// Request info
method := ctx.Method() // GET, POST, etc.
path := ctx.Path() // /news/123
host := ctx.Host() // example.com
// Headers
userAgent := ctx.GetHeader("User-Agent")
ctx.SetHeader("X-Custom", "value")
// Cookies
value := ctx.Cookie("session")
ctx.SetCookie("session", "value", 3600)
// Client IP
ip := ctx.ClientIP()
// Store data in context
ctx.Set("user", user)
user := ctx.Get("user")
}
Route Organization
Modular Route Files
// backend/route/news.go
package route
func SetupNewsRoutes(a *app.App) {
news := a.Group("/news")
news.GET("", ListHandler)
news.GET("/:id", DetailHandler)
news.GET("/category/:cat", CategoryHandler)
}
// backend/route/football.go
func SetupFootballRoutes(a *app.App) {
football := a.Group("/football")
football.GET("", ListHandler)
football.GET("/:id", DetailHandler)
}
// backend/route/route.go
func Setup(a *app.App) {
SetupNewsRoutes(a)
SetupFootballRoutes(a)
SetupAPIRoutes(a)
}
Advanced Routing
Regular Expression Routes
// Match numeric IDs only
a.GET("/news/:id([0-9]+)", NewsHandler)
// Match slugs
a.GET("/post/:slug([a-z0-9-]+)", PostHandler)
// Match year/month/day
a.GET("/archive/:year([0-9]{4})/:month([0-9]{2})", ArchiveHandler)
Wildcard Routes
// Match everything after /files/
a.GET("/files/*filepath", FileHandler)
func FileHandler(ctx *app.Context) {
filepath := ctx.Param("filepath")
// filepath contains: /path/to/file.txt
}
Method-Specific Handlers
// Same path, different methods
a.GET("/resource", GetResource)
a.POST("/resource", CreateResource)
a.PUT("/resource/:id", UpdateResource)
a.DELETE("/resource/:id", DeleteResource)
a.PATCH("/resource/:id", PatchResource)
Best Practices
- RESTful Design - Follow REST conventions for API routes
- Consistent Naming - Use clear, consistent route naming
- Route Grouping - Organize related routes into groups
- Middleware Order - Apply middleware in logical order
- Error Handling - Always handle errors gracefully
- Input Validation - Validate all input parameters
- Security - Implement authentication and authorization
- Documentation - Document your routes and their parameters
Example: Complete Route Setup
package route
import "weber/backend/app"
func Setup(a *app.App) {
// Global middleware
a.Use(LoggerMiddleware)
a.Use(RecoveryMiddleware)
a.Use(CORSMiddleware)
// Static files
a.Static("/static", "./webroot/static")
// Public pages
a.GET("/", IndexHandler)
a.GET("/about", AboutHandler)
a.GET("/contact", ContactHandler)
a.POST("/contact", ContactSubmitHandler)
// News section
news := a.Group("/news")
{
news.GET("", NewsListHandler)
news.GET("/:id", NewsDetailHandler)
news.GET("/category/:category", NewsCategoryHandler)
news.GET("/more", NewsMoreHandler)
}
// Football section
football := a.Group("/football")
{
football.GET("", FootballHandler)
football.GET("/:slug", FootballDetailHandler)
}
// API routes
api := a.Group("/api", RateLimitMiddleware)
{
api.GET("/news", APINewsHandler)
api.GET("/football", APIFootballHandler)
api.POST("/search", APISearchHandler)
}
// Admin routes (protected)
admin := a.Group("/admin", AuthMiddleware)
{
admin.GET("/dashboard", AdminDashboardHandler)
admin.GET("/content", AdminContentHandler)
admin.POST("/publish", AdminPublishHandler)
admin.DELETE("/content/:id", AdminDeleteHandler)
}
// Custom 404
a.NoRoute(NotFoundHandler)
}