@@ -801,20 +801,27 @@ func TestGroup_RouteNotFoundWithMiddleware(t *testing.T) {
801801 expectMiddlewareCalled : true , // because RouteNotFound is added after middleware is added
802802 },
803803 {
804- name : "ok, default group 404 handler is called with middleware" ,
804+ // #2485: a group that auto-registers 404 routes (because it has
805+ // middleware) falls back to the Router's configured NotFoundHandler,
806+ // NOT to a later e.RouteNotFound("/*", ...) call. The group's nil
807+ // handler is resolved to r.notFoundHandler at registration time, and
808+ // e.RouteNotFound only adds a tree route that the group's own catch-all
809+ // shadows. To customise the group 404, configure RouterConfig.NotFoundHandler
810+ // (see TestGroup_RouteNotFoundUsesRouterConfig).
811+ name : "ok, group auto-404 uses configured NotFoundHandler, not later e.RouteNotFound" ,
805812 givenCustom404 : false ,
806813 whenURL : "/group/test3" ,
807814 expectBody : "404 (global) GET /group/*" ,
808815 expectCode : http .StatusNotFound ,
809- expectMiddlewareCalled : true , // because RouteNotFound is added before middleware is added
816+ expectMiddlewareCalled : true , // group middleware still wraps the auto 404 route
810817 },
811818 {
812- name : "ok, (no slash) default group 404 handler is called with middleware " ,
819+ name : "ok, (no slash) group auto- 404 uses configured NotFoundHandler, not later e.RouteNotFound " ,
813820 givenCustom404 : false ,
814821 whenURL : "/group" ,
815822 expectBody : "404 (global) GET /group" ,
816823 expectCode : http .StatusNotFound ,
817- expectMiddlewareCalled : true , // because RouteNotFound is added before middleware is added
824+ expectMiddlewareCalled : true , // group middleware still wraps the auto 404 route
818825 },
819826 }
820827 for _ , tc := range testCases {
@@ -937,3 +944,41 @@ func TestGroup_UseMultipleTimes(t *testing.T) {
937944 })
938945
939946}
947+
948+ // TestGroup_RouteNotFoundUsesRouterConfig documents the supported way to
949+ // customise the 404 handler for groups that auto-register catch-all routes
950+ // (i.e. groups that have middleware). Per maintainer guidance on #2485/#3052,
951+ // adding a route via e.RouteNotFound must NOT have side effects on the Router;
952+ // instead configure RouterConfig.NotFoundHandler, which the group's auto 404
953+ // routes resolve to at registration time and which group middleware wraps.
954+ func TestGroup_RouteNotFoundUsesRouterConfig (t * testing.T ) {
955+ customNotFound := func (c * Context ) error {
956+ return c .String (http .StatusNotFound , "custom-404 " + c .Request ().Method + " " + c .Path ())
957+ }
958+
959+ e := NewWithConfig (Config {
960+ Router : NewRouter (RouterConfig {
961+ NotFoundHandler : customNotFound ,
962+ }),
963+ })
964+
965+ middlewareCalled := false
966+ g := e .Group ("/v0" )
967+ g .Use (func (next HandlerFunc ) HandlerFunc {
968+ return func (c * Context ) error {
969+ middlewareCalled = true
970+ return next (c )
971+ }
972+ })
973+ g .POST ("/resource" , func (c * Context ) error { return c .NoContent (http .StatusOK ) })
974+
975+ // Unmatched path inside the group: the group's auto-registered catch-all
976+ // resolves to the configured NotFoundHandler, and group middleware wraps it.
977+ req := httptest .NewRequest (http .MethodPost , "/v0/missing" , nil )
978+ rec := httptest .NewRecorder ()
979+ e .ServeHTTP (rec , req )
980+
981+ assert .Equal (t , http .StatusNotFound , rec .Code )
982+ assert .Equal (t , "custom-404 POST /v0/*" , rec .Body .String ())
983+ assert .True (t , middlewareCalled , "group middleware must wrap the auto 404 route" )
984+ }
0 commit comments