README
¶
Handler Types Example
This example demonstrates all the different handler types available in the rtr router, showcasing how each handler type simplifies response generation by automatically setting appropriate Content-Type headers and handling response writing.
Features
- Complete Handler Coverage - All 9 handler types: Handler, StringHandler, HTMLHandler, JSONHandler, CSSHandler, XMLHandler, TextHandler, JSHandler, ErrorHandler
- ToHandler Function Demo - Shows how to convert StringHandler to standard Handler
- Handler Priority System - Demonstrates how handlers are prioritized when multiple are set
- URL Parameters - Shows parameter extraction with different handler types
- Interactive Web Interface - Beautiful HTML overview of all examples
- Error Handling Examples - Proper error responses with ErrorHandler
- Comprehensive Testing - Full test coverage for all handler types
Quick Start
-
Run the example:
go run main.go -
Open your browser:
http://localhost:8080 -
Explore the examples:
- Click through the interactive interface to see each handler type in action
- Or use the direct endpoints listed below
-
Run tests:
go test -v
Complete Handler Types
1. Traditional Handler
Standard func(w http.ResponseWriter, r *http.Request) handler with full control over the response.
r.AddRoute(rtr.NewRoute().
SetPath("/traditional").
SetHandler(func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write([]byte("<h1>Full Control</h1>"))
}))
Endpoint: GET /traditional
2. StringHandler
Returns string without automatically setting any headers. Gives you full control over headers.
r.AddRoute(rtr.NewRoute().
SetPath("/raw").
SetStringHandler(func(w http.ResponseWriter, req *http.Request) string {
w.Header().Set("X-Custom-Header", "Raw Response")
return "Raw string response without automatic Content-Type headers."
}))
Endpoint: GET /raw
3. HTMLHandler
Returns HTML string, automatically sets Content-Type: text/html; charset=utf-8.
r.AddRoute(rtr.NewRoute().
SetPath("/html").
SetHTMLHandler(func(w http.ResponseWriter, req *http.Request) string {
return `<h1>HTML Handler</h1><p>Just return HTML string!</p>`
}))
Endpoints:
GET /html- Static HTML exampleGET /user/:id- Dynamic HTML with URL parameters
4. JSONHandler
Returns JSON string, automatically sets Content-Type: application/json.
r.AddRoute(rtr.NewRoute().
SetPath("/api/users").
SetJSONHandler(func(w http.ResponseWriter, req *http.Request) string {
return `{"users": ["alice", "bob", "charlie"]}`
}))
Endpoints:
GET /api/users- Static JSON exampleGET /api/status- Dynamic JSON with timestampGET /api/user/:id- JSON with URL parameters
5. CSSHandler
Returns CSS string, automatically sets Content-Type: text/css.
r.AddRoute(rtr.NewRoute().
SetPath("/styles.css").
SetCSSHandler(func(w http.ResponseWriter, req *http.Request) string {
return `body { font-family: Arial, sans-serif; }`
}))
Endpoint: GET /styles.css
6. XMLHandler
Returns XML string, automatically sets Content-Type: application/xml.
r.AddRoute(rtr.NewRoute().
SetPath("/api/data.xml").
SetXMLHandler(func(w http.ResponseWriter, req *http.Request) string {
return `<?xml version="1.0"?><data><users>...</users></data>`
}))
Endpoint: GET /api/data.xml
7. TextHandler
Returns plain text, automatically sets Content-Type: text/plain; charset=utf-8.
r.AddRoute(rtr.NewRoute().
SetPath("/robots.txt").
SetTextHandler(func(w http.ResponseWriter, req *http.Request) string {
return `User-agent: *\nDisallow: /admin/`
}))
Endpoint: GET /robots.txt
8. JSHandler
Returns JavaScript string, automatically sets Content-Type: application/javascript.
r.AddRoute(rtr.NewRoute().
SetPath("/script.js").
SetJSHandler(func(w http.ResponseWriter, req *http.Request) string {
return `console.log('Hello from RTR Router!');`
}))
Endpoint: GET /script.js
9. ErrorHandler
Returns error for proper error handling. Allows you to return errors that can be handled appropriately.
r.AddRoute(rtr.NewRoute().
SetPath("/error-demo").
SetErrorHandler(func(w http.ResponseWriter, req *http.Request) error {
if someCondition {
w.WriteHeader(http.StatusInternalServerError)
return fmt.Errorf("something went wrong")
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status": "ok"}`))
return nil // nil means success
}))
Endpoints:
GET /error-demo- Success case (returns nil error)GET /error-demo?fail=true- Error case (returns error)GET /not-found-demo- 404 error example
10. ToHandler Function
Utility function that converts any StringHandler to a standard Handler.
r.AddRoute(rtr.NewRoute().
SetPath("/to-handler-demo").
SetHandler(rtr.ToHandler(func(w http.ResponseWriter, req *http.Request) string {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
return `<h1>Converted Handler</h1>`
})))
Endpoint: GET /to-handler-demo
Handler Priority System
When multiple handlers are set on the same route, the router uses this priority order:
- Handler (traditional HTTP handler) - Highest priority
- StringHandler
- HTMLHandler
- JSONHandler
- CSSHandler
- XMLHandler
- TextHandler
- JSHandler
- ErrorHandler - Lowest priority
Priority Demo
The /priority-demo endpoint demonstrates this by setting both HTMLHandler and JSONHandler:
r.AddRoute(rtr.NewRoute().
SetPath("/priority-demo").
SetHTMLHandler(func(w http.ResponseWriter, req *http.Request) string {
return "<h1>HTML Handler</h1><p>HTMLHandler has higher priority</p>"
}).
SetJSONHandler(func(w http.ResponseWriter, req *http.Request) string {
return `{"message": "This won't be returned due to priority"}`
}))
Result: HTMLHandler executes because it has higher priority than JSONHandler.
All Available Endpoints
| Endpoint | Handler Type | Description |
|---|---|---|
GET / |
Traditional | Interactive overview of all examples |
GET /traditional |
Handler | Standard HTTP handler example |
GET /raw |
StringHandler | Raw string without automatic headers |
GET /html |
HTMLHandler | Static HTML generation |
GET /user/:id |
HTMLHandler | Dynamic HTML with parameters |
GET /api/users |
JSONHandler | Static JSON response |
GET /api/status |
JSONHandler | Dynamic JSON with timestamp |
GET /api/user/:id |
JSONHandler | JSON response with parameters |
GET /styles.css |
CSSHandler | CSS stylesheet generation |
GET /api/data.xml |
XMLHandler | XML data response |
GET /robots.txt |
TextHandler | Plain text file |
GET /script.js |
JSHandler | JavaScript file generation |
GET /error-demo |
ErrorHandler | Success case (nil error) |
GET /error-demo?fail=true |
ErrorHandler | Error case (returns error) |
GET /not-found-demo |
ErrorHandler | 404 error example |
GET /to-handler-demo |
ToHandler | StringHandler converted to Handler |
GET /priority-demo |
Multiple | Handler priority demonstration |
URL Parameters
All handler types support URL parameters through the standard router parameter extraction:
// HTMLHandler with parameters
r.AddRoute(rtr.NewRoute().
SetPath("/user/:id").
SetHTMLHandler(func(w http.ResponseWriter, req *http.Request) string {
userID := rtr.MustGetParam(req, "id")
return fmt.Sprintf("<h1>User %s</h1>", userID)
}))
// JSONHandler with parameters
r.AddRoute(rtr.NewRoute().
SetPath("/api/user/:id").
SetJSONHandler(func(w http.ResponseWriter, req *http.Request) string {
userID := rtr.MustGetParam(req, "id")
return fmt.Sprintf(`{"user_id": "%s"}`, userID)
}))
Testing
The example includes comprehensive tests covering:
- All Handler Types - Verify each handler type works correctly
- Content-Type Headers - Ensure proper headers are set automatically
- URL Parameters - Test parameter extraction with different handlers
- Handler Priority - Verify priority system works as expected
- Error Handling - Test ErrorHandler success and error cases
- ToHandler Function - Verify conversion works properly
- Response Content - Validate actual response content
Run tests with:
go test -v # Run all tests
go test -run TestHTML # Run specific handler tests
go test -cover # Run with coverage
Benefits of Specialized Handlers
Simplified Code
Instead of manually setting headers and writing responses:
// Traditional approach
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}
// With JSONHandler
func(w http.ResponseWriter, r *http.Request) string {
return `{"message": "Hello World"}`
}
Automatic Content-Type Handling
- No need to remember correct MIME types
- Consistent header setting across your application
- Reduced boilerplate code
Better Error Handling
ErrorHandler provides a clean way to handle errors:
// Traditional error handling
func(w http.ResponseWriter, r *http.Request) {
if err := doSomething(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Error occurred"))
return
}
w.Write([]byte("Success"))
}
// With ErrorHandler
func(w http.ResponseWriter, r *http.Request) error {
if err := doSomething(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return err
}
w.Write([]byte("Success"))
return nil
}
Better Code Organization
- Clear separation of concerns
- Handler type indicates response format
- Easier to understand and maintain
Advanced Usage
Combining with Middleware
All handler types work seamlessly with middleware:
r.AddRoute(rtr.NewRoute().
SetPath("/api/data").
SetJSONHandler(jsonHandler).
SetBeforeMiddleware(authMiddleware, loggingMiddleware))
Dynamic Content Generation
Handlers can generate dynamic content using any Go functionality:
SetJSONHandler(func(w http.ResponseWriter, req *http.Request) string {
data := fetchFromDatabase()
return generateJSON(data)
})
Custom Headers with StringHandler
StringHandler gives you full control over headers:
SetStringHandler(func(w http.ResponseWriter, req *http.Request) string {
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("X-Custom-Header", "value")
return "Response with custom headers"
})
Error Handling Patterns
ErrorHandler supports various error handling patterns:
SetErrorHandler(func(w http.ResponseWriter, req *http.Request) error {
// Validation
if err := validateRequest(req); err != nil {
w.WriteHeader(http.StatusBadRequest)
return err
}
// Business logic
result, err := processRequest(req)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return err
}
// Success
w.Header().Set("Content-Type", "application/json")
w.Write(result)
return nil
})
ToHandler Use Cases
ToHandler is useful when you need to convert a StringHandler to a standard Handler:
// When you have a StringHandler but need a Handler
stringHandler := func(w http.ResponseWriter, r *http.Request) string {
return "Hello World"
}
// Convert to Handler
handler := rtr.ToHandler(stringHandler)
// Use with middleware that expects Handler
middlewareFunc(handler)
Best Practices
- Choose the Right Handler Type - Use the handler that matches your response format
- Use ErrorHandler for Error Cases - Provides cleaner error handling patterns
- StringHandler for Custom Headers - When you need full control over headers
- Keep Handlers Simple - Complex logic should be in separate functions
- Handle Parameters Safely - Validate and sanitize URL parameters
- Test All Handler Types - Ensure proper behavior and headers
- Document Handler Behavior - Make it clear what each endpoint returns
- Use ToHandler When Needed - For converting StringHandlers to Handlers
Handler Type Selection Guide
| Use Case | Recommended Handler | Reason |
|---|---|---|
| HTML pages | HTMLHandler | Automatic Content-Type, clean syntax |
| JSON APIs | JSONHandler | Automatic Content-Type, clean syntax |
| CSS files | CSSHandler | Proper MIME type, browser compatibility |
| JavaScript files | JSHandler | Proper MIME type, browser compatibility |
| XML responses | XMLHandler | Proper MIME type for XML consumers |
| Plain text | TextHandler | Proper encoding, simple syntax |
| Custom headers needed | StringHandler | Full control over response headers |
| Error handling | ErrorHandler | Clean error patterns, proper status codes |
| Complex responses | Handler | Full control when needed |
| Converting handlers | ToHandler | Bridge between StringHandler and Handler |
Troubleshooting
Common Issues
-
Wrong Content-Type
- Ensure you're using the correct handler type for your content
- Traditional handlers require manual header setting
-
Handler Not Executing
- Check handler priority if multiple handlers are set
- Verify route path and method match
-
Parameters Not Found
- Ensure parameter names in path match extraction calls
- Use
rtr.GetParam()for optional parameters
-
Headers Not Set
- StringHandler doesn't set headers automatically
- Use other handler types for automatic header setting
-
Error Handling Issues
- ErrorHandler requires returning error for error cases
- Return nil for success cases
Debug Tips
- Check Handler Priority - Use
/priority-demoto understand precedence - Inspect Headers - Use browser dev tools to verify Content-Type
- Test Incrementally - Start with simple handlers and add complexity
- Use Logging - Add logging to see which handlers execute
- Test Error Cases - Verify ErrorHandler behavior with different scenarios
Related Examples
- Declarative Example - Declarative router configuration
- Domain Example - Domain-based routing
- Basic Example - Simple router usage
Contributing
When contributing to this example:
- Add tests for new handler types
- Update the interactive HTML interface
- Document new features in this README
- Follow Go conventions and best practices
- Ensure all handler types are covered
Documentation
¶
There is no documentation for this package.