121 lines
2.5 KiB
Markdown
121 lines
2.5 KiB
Markdown
# sux
|
|
|
|
An allocation-conscious, middleware-capable HTTP router for Go with support for static routes, parameters (`:id`), wildcards (`*path`), route groups, and configurable 404/405 handling.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
go get code.icod.de/dalu/sux
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"code.icod.de/dalu/sux"
|
|
)
|
|
|
|
func main() {
|
|
r := sux.New()
|
|
|
|
r.GET("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("hello"))
|
|
})
|
|
|
|
r.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
|
|
params := sux.ParamsFromContext(r)
|
|
w.Write([]byte("user " + params.Get("id")))
|
|
})
|
|
|
|
http.ListenAndServe(":8080", r)
|
|
}
|
|
```
|
|
|
|
## Middleware
|
|
|
|
Middleware wraps handlers and can be applied globally or per route group.
|
|
|
|
```go
|
|
logger := func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// log request here
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
r := sux.New()
|
|
r.Use(logger) // global
|
|
|
|
r.GET("/ping", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("pong"))
|
|
})
|
|
```
|
|
|
|
## Route groups
|
|
|
|
Groups share a prefix and middleware.
|
|
|
|
```go
|
|
api := r.Group("/api", func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-API-Version", "v1")
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
})
|
|
|
|
api.GET("/users", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("users"))
|
|
})
|
|
```
|
|
|
|
## Parameters and wildcards
|
|
|
|
```go
|
|
r.GET("/posts/:postId", func(w http.ResponseWriter, r *http.Request) {
|
|
p := sux.ParamsFromContext(r)
|
|
w.Write([]byte(p.Get("postId")))
|
|
})
|
|
|
|
r.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) {
|
|
p := sux.ParamsFromContext(r)
|
|
w.Write([]byte("file: " + p.Get("path")))
|
|
})
|
|
```
|
|
|
|
## 404/405 handlers
|
|
|
|
```go
|
|
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "custom 404", http.StatusNotFound)
|
|
})
|
|
|
|
r.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Error(w, "custom 405", http.StatusMethodNotAllowed)
|
|
})
|
|
|
|
// Disable cross-method probing if you prefer 404s instead of 405 checks
|
|
r.EnableMethodNotAllowedCheck(false)
|
|
```
|
|
|
|
## Performance notes
|
|
|
|
- Internally pools parsed segments and parameter storage to reduce per-request allocations.
|
|
- Parameters are copied into the request context so they remain valid even after handlers return (important for async users of the context).
|
|
- For raw numbers, run `go test -bench . -benchmem`.
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
Benchmarks:
|
|
|
|
```bash
|
|
go test -bench . -benchmem
|
|
```
|