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
staticfolder 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)
}