3434import java .util .Objects ;
3535import java .util .Optional ;
3636
37+ import jakarta .servlet .http .HttpServletRequest ;
38+ import jakarta .servlet .http .HttpServletRequestWrapper ;
3739import org .apache .commons .logging .Log ;
3840import org .apache .commons .logging .LogFactory ;
3941import org .jspecify .annotations .Nullable ;
@@ -123,6 +125,21 @@ public static <T> Optional<T> cacheAndReadBody(ServerRequest request, Class<T> t
123125 return readBody (request , rawBody , toClass );
124126 }
125127
128+ /**
129+ * Unwraps an HttpServletRequest to get to the innermost request. This is necessary to
130+ * ensure attributes are set on the actual request object rather than on wrapper
131+ * instances that may not propagate attribute changes.
132+ * @param request the request to unwrap
133+ * @return the innermost HttpServletRequest
134+ */
135+ private static HttpServletRequest unwrapRequest (HttpServletRequest request ) {
136+ HttpServletRequest unwrapped = request ;
137+ while (unwrapped instanceof HttpServletRequestWrapper wrapper ) {
138+ unwrapped = (HttpServletRequest ) wrapper .getRequest ();
139+ }
140+ return unwrapped ;
141+ }
142+
126143 public static ByteArrayInputStream cacheBody (ServerRequest request ) {
127144 try {
128145 byte [] bytes = StreamUtils .copyToByteArray (request .servletRequest ().getInputStream ());
@@ -187,7 +204,12 @@ public static ApplicationContext getApplicationContext(ServerRequest request) {
187204 if (request .attributes ().containsKey (key )) {
188205 return (T ) request .attributes ().get (key );
189206 }
190- return (T ) getGatewayAttributes (request ).get (key );
207+ T gatewayAttr = (T ) getGatewayAttributes (request ).get (key );
208+ if (gatewayAttr != null ) {
209+ return gatewayAttr ;
210+ }
211+ // Fallback to servlet request attributes for compatibility with request wrappers
212+ return (T ) request .servletRequest ().getAttribute (key );
191213 }
192214
193215 @ SuppressWarnings ("unchecked" )
@@ -211,13 +233,18 @@ public static Map<String, Object> getUriTemplateVariables(ServerRequest request)
211233 }
212234
213235 public static void putAttribute (ServerRequest request , String key , @ Nullable Object value ) {
236+ // Also set on the unwrapped servlet request to ensure persistence through
237+ // wrappers like Spring's AttributesPreservingRequest
238+ HttpServletRequest unwrapped = unwrapRequest ((HttpServletRequest ) request .servletRequest ());
214239 if (value == null ) {
215240 request .attributes ().remove (key );
216241 getGatewayAttributes (request ).remove (key );
242+ unwrapped .removeAttribute (key );
217243 }
218244 else {
219245 request .attributes ().put (key , value );
220246 getGatewayAttributes (request ).put (key , value );
247+ unwrapped .setAttribute (key , value );
221248 }
222249
223250 }
0 commit comments