Static router
The ogen
tool generates a static blazing fast radix tree-based router.
Router error handling
Not Found
If there is no route for the request, router calls the config.NotFound
handler.
The default handler is http.NotFound
.
You can change the default handler by using WithNotFound
option.
h := myHandler{}
srv, err := api.NewServer(h, api.WithNotFound(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
_, _ = io.WriteString(w, `{"error": "not found"}`)
}))
if err != nil {
panic(err)
}
Method Not Allowed
If request path matches but the method is not allowed, router calls the config.MethodNotAllowed
handler.
The default handler returns empty body
with 405 Method Not Allowed
code
and Allow
header with comma-separated allowed methods list.
You can change the default handler by using WithMethodNotAllowed
option.
The HTTP spec requires that the Allow
header
is sent with the status code 405 Method Not Allowed
.
You should add the Allow
header to the response by yourself.
h := myHandler{}
srv, err := api.NewServer(h, api.WithMethodNotAllowed(func(w http.ResponseWriter, r *http.Request, allowed string) {
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Allow", allowed)
w.WriteHeader(http.StatusMethodNotAllowed)
_, _ = fmt.Fprintf(w, "use one of the following methods: %s\n", allowed)
}))
if err != nil {
panic(err)
}
Using net/http
middlewares
The Server
type implements http.Handler
interface. Any net/http
-compatible
middleware can be used.
Applying middleware to all routes
package main
import (
"net/http"
"github.com/klauspost/compress/gzhttp"
api "<your_api_package>"
)
func main() {
srv, err := api.NewServer(myHandler{})
if err != nil {
panic(err)
}
http.ListenAndServe(":8080", gzhttp.GzipHandler(srv))
}
Using FindRoute
method
The Server
type defines a FindRoute
method that finds and returns the Route
for the request, if any.
It's useful for middleware that needs to find the route for the request.
package main
import (
"net/http"
api "<your_api_package>"
)
type myMiddleware struct {
Next *api.Server
}
func (m myMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
route, ok := m.Next.FindRoute(r.Method, r.URL.Path)
if !ok {
// There is no route for the request.
//
// Let server handle 404/405.
m.Next.ServeHTTP(w, r)
return
}
// Match operation by spec operation ID.
// Notice that the operation ID is optional and may be empty.
//
// You can also use `route.Name()` to get the ogen operation name.
// Unlike the operation ID, the name is guaranteed to be unique and non-empty.
switch route.OperationID() {
case "operation1", "operation2":
// Middleware logic:
args := route.Args()
if args[0] == "debug" {
w.Header().Set("X-Debug", "true")
}
}
m.Next.ServeHTTP(w, r)
}
func main() {
s, err := api.NewServer(myHandler{})
if err != nil {
panic(err)
}
http.ListenAndServe(":8080", myMiddleware{Next: s})
}
Adding profiler, metrics or static content route
To serve other routes, you can use any upper-level router.
Example would serve
- API routes at
/api/v1/*
- pprof handlers on
/debug/pprof/*
- Files from
static
folder on/static/*
- Prometheus metrics handler on
/metrics
- net/http
- chi
package main
import (
"embed"
"net/http"
"net/http/pprof"
"github.com/prometheus/client_golang/prometheus/promhttp"
api "<your_api_package>"
)
//go:embed static
var static embed.FS
func main() {
srv, err := api.NewServer(myHandler{})
if err != nil {
panic(err)
}
mux := http.NewServeMux()
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", srv))
// Register static files.
mux.Handle("/static/", http.FileServer(http.FS(static)))
// Register metrics handler.
mux.Handle("/metrics", promhttp.Handler())
{
// Register pprof handlers.
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
}
http.ListenAndServe(":8080", mux)
}
package main
import (
"embed"
"net/http"
"net/http/pprof"
"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus/promhttp"
api "<your_api_package>"
)
//go:embed static
var static embed.FS
func main() {
srv, err := api.NewServer(myHandler{})
if err != nil {
panic(err)
}
mux := chi.NewRouter()
mux.Mount("/api/v1/", http.StripPrefix("/api/v1", srv))
// Register static files.
mux.Handle("/static/", http.FileServer(http.FS(static)))
// Register metrics handler.
mux.Handle("/metrics", promhttp.Handler())
mux.Route("/debug/pprof", func(mux chi.Router) {
// Register pprof handlers.
mux.HandleFunc("/", pprof.Index)
mux.HandleFunc("/cmdline", pprof.Cmdline)
mux.HandleFunc("/profile", pprof.Profile)
mux.HandleFunc("/symbol", pprof.Symbol)
mux.HandleFunc("/trace", pprof.Trace)
})
http.ListenAndServe(":8080", mux)
}