Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ func (r *Action) Name(name string) contractsroute.Action {

return r
}

func (r *Action) WithoutMiddleware(middleware ...contractshttp.Middleware) contractsroute.Action {
info := routes[r.path][r.method]
info.ExcludedMiddleware = append(info.ExcludedMiddleware, middleware...)
routes[r.path][r.method] = info

return r
}
6 changes: 3 additions & 3 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ type sharedValuesKeyType struct{}
type sharedUserCtxKeyType struct{}

var (
sessionKey = sessionKeyType{}
sharedValuesKey = sharedValuesKeyType{}
sharedUserCtxKey = sharedUserCtxKeyType{}
sessionKey = sessionKeyType{}
sharedValuesKey = sharedValuesKeyType{}
sharedUserCtxKey = sharedUserCtxKeyType{}
)

func Background() http.Context {
Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.25.6
require (
github.com/gofiber/fiber/v3 v3.3.0
github.com/gofiber/utils/v2 v2.1.0
github.com/goravel/framework v1.17.2-0.20260620091942-4f2585d35807
github.com/goravel/framework v1.17.2-0.20260621064957-8c973e865adb
github.com/spf13/cast v1.10.0
github.com/stretchr/testify v1.11.1
github.com/valyala/fasthttp v1.71.0
Expand Down Expand Up @@ -126,3 +126,6 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/gorm v1.31.1 // indirect
)

// TODO: remove after framework PR is merged
replace github.com/goravel/framework => github.com/u-wlkjyy/framework v1.17.2-0.20260621064957-8c973e865adb
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/goravel/framework v1.17.2-0.20260620091942-4f2585d35807 h1:ghACsFUv+iwFYEh5AlhCMONb8tu3iL9vQTB3BZeBeEg=
github.com/goravel/framework v1.17.2-0.20260620091942-4f2585d35807/go.mod h1:J3xvbbFAbS/sYePrlIMfUD6anGcrnE49mnntYlKUvls=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
Expand Down Expand Up @@ -236,6 +234,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/u-wlkjyy/framework v1.17.2-0.20260621064957-8c973e865adb h1:052mJgGyRtXRfLrpcMpTZMRwQr+ogVdABfHdZrxd/rk=
github.com/u-wlkjyy/framework v1.17.2-0.20260621064957-8c973e865adb/go.mod h1:J3xvbbFAbS/sYePrlIMfUD6anGcrnE49mnntYlKUvls=
github.com/urfave/cli/v3 v3.10.0 h1:0aU8yOObVDMkM13Cj4G+zb4P0PdeJMec65f81Ak1ioM=
github.com/urfave/cli/v3 v3.10.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
Expand Down
75 changes: 66 additions & 9 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import (
)

type Group struct {
config config.Config
instance *fiber.App
prefix string
middlewares []contractshttp.Middleware
lastMiddlewares []contractshttp.Middleware
config config.Config
instance *fiber.App
prefix string
middlewares []contractshttp.Middleware
lastMiddlewares []contractshttp.Middleware
excludedMiddlewares []contractshttp.Middleware
}

func NewGroup(config config.Config, instance *fiber.App, prefix string, middlewares []contractshttp.Middleware, lastMiddlewares []contractshttp.Middleware) contractsroute.Router {
Expand All @@ -36,15 +37,47 @@ func NewGroup(config config.Config, instance *fiber.App, prefix string, middlewa
}

func (r *Group) Group(handler contractsroute.GroupFunc) {
handler(NewGroup(r.config, r.instance, r.getFullPath(""), r.middlewares, r.lastMiddlewares))
handler(&Group{
config: r.config,
instance: r.instance,
prefix: r.getFullPath(""),
middlewares: r.middlewares,
lastMiddlewares: r.lastMiddlewares,
excludedMiddlewares: r.excludedMiddlewares,
})
}

func (r *Group) Prefix(path string) contractsroute.Router {
return NewGroup(r.config, r.instance, r.getFullPath(path), r.middlewares, r.lastMiddlewares)
return &Group{
config: r.config,
instance: r.instance,
prefix: r.getFullPath(path),
middlewares: r.middlewares,
lastMiddlewares: r.lastMiddlewares,
excludedMiddlewares: r.excludedMiddlewares,
}
}

func (r *Group) Middleware(middlewares ...contractshttp.Middleware) contractsroute.Router {
return NewGroup(r.config, r.instance, r.getFullPath(""), append(r.middlewares, middlewares...), r.lastMiddlewares)
return &Group{
config: r.config,
instance: r.instance,
prefix: r.getFullPath(""),
middlewares: append(r.middlewares, middlewares...),
lastMiddlewares: r.lastMiddlewares,
excludedMiddlewares: r.excludedMiddlewares,
}
}

func (r *Group) WithoutMiddleware(middlewares ...contractshttp.Middleware) contractsroute.Router {
return &Group{
config: r.config,
instance: r.instance,
prefix: r.getFullPath(""),
middlewares: r.middlewares,
lastMiddlewares: r.lastMiddlewares,
excludedMiddlewares: append(r.excludedMiddlewares, middlewares...),
}
}

func (r *Group) Any(path string, handler contractshttp.HandlerFunc) contractsroute.Action {
Expand Down Expand Up @@ -157,14 +190,38 @@ func (h httpFSToFS) Open(name string) (fs.File, error) {

func (r *Group) getMiddlewares(handler contractshttp.HandlerFunc) []fiber.Handler {
var middlewares []fiber.Handler
middlewares = middlewaresToFiberHandlers(append(r.middlewares, r.lastMiddlewares...))
middlewares = middlewaresToFiberHandlers(r.excludeMiddlewares(append(r.middlewares, r.lastMiddlewares...)))
if handler != nil {
middlewares = append(middlewares, handlerToFiberHandler(handler))
}

return middlewares
}

// excludeMiddlewares filters out middlewares excluded via WithoutMiddleware,
// comparing by reflect.Type (see isSameMiddleware in utils.go).
func (r *Group) excludeMiddlewares(middlewares []contractshttp.Middleware) []contractshttp.Middleware {
if len(r.excludedMiddlewares) == 0 {
return middlewares
}

var result []contractshttp.Middleware
for _, middleware := range middlewares {
excluded := false
for _, ex := range r.excludedMiddlewares {
if isSameMiddleware(ex, middleware) {
excluded = true
break
}
}
if !excluded {
result = append(result, middleware)
}
}

return result
}

func (r *Group) getMiddlewaresWithPath(path string, handler contractshttp.HandlerFunc) []any {
var handlers []any
handlers = append(handlers, path)
Expand Down
15 changes: 15 additions & 0 deletions group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,21 @@ func (s *GroupTestSuite) TestIssue408() {
s.Equal("/prefix/{id}/test/{name}", routes[1].Path)
}

func (s *GroupTestSuite) TestWithoutMiddleware() {
mw := func(ctx contractshttp.Context) {
ctx.WithValue("mw", "applied")
ctx.Request().Next()
}

s.route.Middleware(mw).WithoutMiddleware(mw).Get("/without", func(ctx contractshttp.Context) contractshttp.Response {
return ctx.Response().Json(http.StatusOK, contractshttp.Json{
"mw": ctx.Value("mw"),
})
})

s.assert("GET", "/without", http.StatusOK, `{"mw":null}`)
}

func (s *GroupTestSuite) assert(method, url string, expectCode int, expectBody string) {
req, err := http.NewRequest(method, url, nil)
s.Nil(err)
Expand Down
2 changes: 1 addition & 1 deletion middleware_timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"errors"
"time"

contractshttp "github.com/goravel/framework/contracts/http"
"github.com/gofiber/fiber/v3"
fibertimeout "github.com/gofiber/fiber/v3/middleware/timeout"
contractshttp "github.com/goravel/framework/contracts/http"
)

// Timeout creates middleware to set a timeout for a request.
Expand Down
20 changes: 20 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fiber

import (
"errors"
"reflect"
"regexp"
"strings"

Expand Down Expand Up @@ -108,3 +109,22 @@ func mergeSlashForPath(path string) string {

return strings.ReplaceAll(path, "//", "/")
}

// isSameMiddleware reports whether two middleware values have the same concrete
// type (dereferencing pointers). This lets WithoutMiddleware match struct-based
// middleware across different instances. Closure-based middleware (func(Context))
// share a single reflect.Type and cannot be told apart — a documented limitation.
func isSameMiddleware(a, b any) bool {
tA := reflect.TypeOf(a)
tB := reflect.TypeOf(b)
if tA == nil || tB == nil {
return false
}
if tA.Kind() == reflect.Pointer {
tA = tA.Elem()
}
if tB.Kind() == reflect.Pointer {
tB = tB.Elem()
}
return tA == tB
}
12 changes: 12 additions & 0 deletions utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ func TestBracketToColon(t *testing.T) {
func TestColonToBracket(t *testing.T) {
assert.Equal(t, "/{id}/{name}", colonToBracket("/:id/:name"))
}

func TestIsSameMiddleware(t *testing.T) {
type mwA struct{}
type mwB struct{}

assert.True(t, isSameMiddleware(&mwA{}, &mwA{}))
assert.False(t, isSameMiddleware(&mwA{}, &mwB{}))
fn1 := func() {}
fn2 := func() {}
assert.True(t, isSameMiddleware(fn1, fn2))
assert.False(t, isSameMiddleware(nil, fn1))
}
Loading