feat: standardize transport contracts across Gin and nethttp
This commit is contained in:
@@ -4,14 +4,23 @@ package gin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
||||
"git.yuntuo.hk/k2safe/go-omni/transport/internal/routing"
|
||||
ginlib "github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Router is the default Gin-backed GoOmni router.
|
||||
type Router struct {
|
||||
engine *ginlib.Engine
|
||||
routes []registeredRoute
|
||||
}
|
||||
|
||||
type registeredRoute struct {
|
||||
method string
|
||||
segments []routing.Segment
|
||||
}
|
||||
|
||||
// New creates a Gin router without Gin's default logger or recovery middleware.
|
||||
@@ -19,11 +28,47 @@ func New(mode string) *Router {
|
||||
if mode != "" {
|
||||
ginlib.SetMode(mode)
|
||||
}
|
||||
return &Router{engine: ginlib.New()}
|
||||
return NewWithEngine(ginlib.New())
|
||||
}
|
||||
|
||||
// NewWithEngine wraps an existing Gin engine for incremental migration.
|
||||
func NewWithEngine(engine *ginlib.Engine) *Router { return &Router{engine: engine} }
|
||||
// NewWithEngine wraps an existing engine and applies GoOmni's routing policy.
|
||||
// Native middleware/routes are retained; redirect and fallback settings are
|
||||
// replaced. Register portable routes through Router, not Engine().
|
||||
func NewWithEngine(engine *ginlib.Engine) *Router {
|
||||
if engine == nil {
|
||||
panic("go-omni/gin: nil engine")
|
||||
}
|
||||
engine.RedirectTrailingSlash = false
|
||||
engine.RedirectFixedPath = false
|
||||
engine.RemoveExtraSlash = false
|
||||
engine.UseRawPath = false
|
||||
// Gin 1.10 can carry backtracking state between method trees during its
|
||||
// 405 lookup. Derive Allow from registered patterns instead of that lookup.
|
||||
engine.HandleMethodNotAllowed = false
|
||||
router := &Router{engine: engine}
|
||||
engine.NoRoute(router.fallback)
|
||||
return router
|
||||
}
|
||||
|
||||
func (r *Router) fallback(ctx *ginlib.Context) {
|
||||
allowed := make(map[string]bool)
|
||||
for _, route := range r.routes {
|
||||
if _, matches := routing.Match(route.segments, ctx.Request.URL.Path); matches {
|
||||
allowed[route.method] = true
|
||||
}
|
||||
}
|
||||
if len(allowed) == 0 {
|
||||
http.NotFound(ctx.Writer, ctx.Request)
|
||||
return
|
||||
}
|
||||
methods := make([]string, 0, len(allowed))
|
||||
for method := range allowed {
|
||||
methods = append(methods, method)
|
||||
}
|
||||
sort.Strings(methods)
|
||||
ctx.Header("Allow", strings.Join(methods, ", "))
|
||||
http.Error(ctx.Writer, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
// Engine exposes the underlying Gin engine to adapter-specific integrations.
|
||||
func (r *Router) Engine() *ginlib.Engine { return r.engine }
|
||||
@@ -33,29 +78,38 @@ func (r *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
func (r *Router) Handle(method, path string, handler http.Handler) {
|
||||
r.engine.Handle(method, path, r.adapt(handler))
|
||||
segments := routing.Parse(method, path, handler)
|
||||
r.engine.Handle(method, path, adapt(handler))
|
||||
r.routes = append(r.routes, registeredRoute{method: method, segments: segments})
|
||||
}
|
||||
|
||||
func (r *Router) Any(path string, handler http.Handler) { r.engine.Any(path, r.adapt(handler)) }
|
||||
func (r *Router) Any(path string, handler http.Handler) {
|
||||
for _, method := range routing.Methods {
|
||||
r.Handle(method, path, handler)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) Group(path string) httpx.RouterGroup {
|
||||
return &group{group: r.engine.Group(path)}
|
||||
return &group{router: r, prefix: routing.Join("", path)}
|
||||
}
|
||||
|
||||
type group struct{ group *ginlib.RouterGroup }
|
||||
type group struct {
|
||||
router *Router
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (g *group) Handle(method, path string, handler http.Handler) {
|
||||
g.group.Handle(method, path, adapt(handler))
|
||||
g.router.Handle(method, routing.Join(g.prefix, path), handler)
|
||||
}
|
||||
|
||||
func (g *group) Any(path string, handler http.Handler) { g.group.Any(path, adapt(handler)) }
|
||||
func (g *group) Any(path string, handler http.Handler) {
|
||||
g.router.Any(routing.Join(g.prefix, path), handler)
|
||||
}
|
||||
|
||||
func (g *group) Group(path string) httpx.RouterGroup {
|
||||
return &group{group: g.group.Group(path)}
|
||||
return &group{router: g.router, prefix: routing.Join(g.prefix, path)}
|
||||
}
|
||||
|
||||
func (r *Router) adapt(handler http.Handler) ginlib.HandlerFunc { return adapt(handler) }
|
||||
|
||||
func adapt(handler http.Handler) ginlib.HandlerFunc {
|
||||
return func(ctx *ginlib.Context) {
|
||||
params := make(map[string]string, len(ctx.Params))
|
||||
|
||||
Reference in New Issue
Block a user