Skip to content

Webflux does not have 'forward' but this is supported by all web servers #34834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
orubel opened this issue Apr 26, 2025 · 16 comments
Closed

Webflux does not have 'forward' but this is supported by all web servers #34834

orubel opened this issue Apr 26, 2025 · 16 comments
Labels
status: invalid An issue that we don't feel is valid

Comments

@orubel
Copy link

orubel commented Apr 26, 2025

Have been searching everywhere and can see how to do an EXTERNAL redirect but NOT an internal one (ie forward)

Where in documentation is an internal redirect(aka forward) being handled??

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 26, 2025
@bclozel
Copy link
Member

bclozel commented Apr 26, 2025

We did not implement this feature on purpose. See #19106

This is documented here: https://docs.spring.io/spring-framework/reference/web/webflux/dispatcher-handler.html#webflux-redirecting-redirect-prefix

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Apr 26, 2025
@bclozel bclozel added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Apr 26, 2025
@orubel
Copy link
Author

orubel commented Apr 27, 2025

Redirects go OUTSIDE the DMZ and are unsafe and time consuming. Internal redirects are safer and have far less latency

So they are not a replacement. More a bandaid fix for people looking to use 'forward'

@bclozel
Copy link
Member

bclozel commented Apr 27, 2025

I'm not saying we consider HTTP redirects as a replacement for forward. Forward is a Servlet specific concept that has no equivalent in the HTTP world.

Supporting this means we would need to support very specific lifecycle behavior for non-Servlet containers like Netty. This is not an easy task and so far we chose and managed to use other means to support various features.

Unless there is a clear and important use case that we cannot support on any way, we are not likely to invest in this area as this would make our stack more complex for little benefit.

@orubel
Copy link
Author

orubel commented Apr 27, 2025

Thank you for that. I was under the impression that since other reactive frameworks can do this (ie node.js) that there was not an issue. And that this functionality has existed since the very first web server, that this functionality would be included.

Also I believe Jetty/Netty both support this too (since its a standard in web servers)
https://blog.swwomm.com/2012/04/jetty-6-https-redirects.html

@bclozel
Copy link
Member

bclozel commented Apr 28, 2025

I think you're conflating Servlet forward dispatches with other concepts. As far as I understand, what Node.JS does is basically using layers of middlewares and delegating the processing of the request/response exchange to different functions. We already support that with WebFilter.

As for the link you're pointing to, this is referring to regular HTTP redirects and HTTP Forwarded headers, which we do support.

@orubel
Copy link
Author

orubel commented Apr 28, 2025

Ok I'm confused. The forward header(X-FORWARDED-FOR) is used WHEN 'forwarding'; I use it for forwarding to my error controller. Are you saying we are supposed to use a REDIRECT for forwarding to error controllers/handlers now?

Also I don't believe a filter handles a forward; it is handled by the front controller/dispatcher in almost all servers.

Additionally, netty/jetty/nodejs and other STILL all have this functionality.

I guess what I am saying is that the functionality has existed since the first web server and still exists in ALL web servers (including reactive ones) ... especially for error handling.

This would definitely be a significant departure from web architecture by not implementing/supporting this.

@bclozel
Copy link
Member

bclozel commented Apr 29, 2025

The forwarded HTTP header is not related to Servlet at all. It's an HTTP header that can be set by HTTP proxies.
The Jetty RequestDispatcher.forward() is the Servlet implementation of that Servlet forward dispatch. This won't work if you're using Jetty without the Servlet integration.

I think this discussion is going full circle with no actionable change on our side. Spring Boot does support WebFlux error handling (similar to MVC) and we don't need Servlet Forward dispatches for that. We're implementing a WebExceptionHandler. The equivalent of the Servlet API in our WebFlux support is described in the WebHandler API section.

It is true that the current WebFlux model relies on a single "dispatch" through the entire chain, but there are many ways to delegate the handling of an HTTP exchange without mutating the incoming request and restarting the entire dispatcher chain. Without a clear description of what you're trying to achieve it's hard for me to point to a particular direction.

@orubel
Copy link
Author

orubel commented Apr 29, 2025

Ok then I guess how would you suggest 'forwarding' to errorHandler/controller without a 'forward'? This is very common practice.

This one puzzles me.

@bclozel
Copy link
Member

bclozel commented Apr 29, 2025

I would suggest implementing an error handler as pointed out earlier in this thread. Forwarding errors to a controller is an artificial construct - you mainly want to handle errors in a global fashion and we're supporting this quite well in Spring Boot without involving any controller with WebFlux.

@orubel
Copy link
Author

orubel commented Apr 29, 2025

The suggestion in multiple docs for Springboot for a custom error handler is to forward to them.
request.getRequestDispatcher("/error").forward(request, response)

@bclozel
Copy link
Member

bclozel commented Apr 29, 2025

That's for Servlet applications. This API is not present in WebFlux applications. If you want to provide the equivalent of a custom error controller in Spring Boot you can contribute a custom ErrorWebExceptionHandler. If you only want to handle one type of exception, then only contributing a WebExceptionHandler and ordering it should be enough.

@orubel
Copy link
Author

orubel commented Apr 29, 2025

"This API is not present in WebFlux applications"

The API IS supported by Netty/Jetty/Tomcat/etc. The webflux team appears to just be choosing not to support web server functionality.

And errors can happen everywhere :filters, interceptors, services, etc. This is why it is important to be able to forward to your error controller.

@bclozel
Copy link
Member

bclozel commented Apr 29, 2025

The Netty ChannelHandler API looks like a pipe/filter mechanism to me. Can you point to a code snippet that would achieve something similar to forward dispatches in Servlet?

the webflux team are just choosing not to support custom errorhandling

I have shown that it's already possible in multiple ways without involving forward dispatches which is an orthogonal concern.

@orubel
Copy link
Author

orubel commented Apr 29, 2025

The two approaches I found were using a builtin proxy or the channelHandler on netty( https://dzone.com/articles/building-a-performant-application-using-netty )

I believe it might have been HttpProxyHandler(??)

Those are the ways people are TRYING to do it with Netty :)

Technically, the dispatcherHandler should handle the routing for this (once netty hands-off) as a forward is handled by the front controller so that it can be passed through filters/interceptors all over again as new endpoint has to run through access/security checks. At least that is my understanding.

I think this is an example of someone trying to do it through Netty(w/websockets) netty/netty#12544

Am I wrong to think this could be handled by a custom ResultHandler or HandlerAdapter???

@orubel
Copy link
Author

orubel commented Apr 29, 2025

There is also a HARDCODED way to kind of due this at the router... But this is mainly a hack and you are mixing functionality all over the place here when you take into consideration 'error handling'

   Mono<ServerResponse> startHandler(ServerRequest serverRequest) {
       return myService.retrieveSomething()
               .flatMap(something -> {
                   if (something.isBlank()) {
                       // ... handle the case when something is blank
                   } else {
                       // Forward the request to continueHandler
                       return continueHandler(serverRequest);
                   }
               });
   }

@orubel
Copy link
Author

orubel commented Apr 30, 2025

so in going over netty documentation, all netty communication is handled through channels. Therefore I'm beginning to think Channel Handler or Dispatcherhandler is the way to address. Either that or a custom event handler.

But since a 'forward'/internal redirect would go out to the server(Netty), it would have to have a way to route after coming back in

I just dont know what the equivalent of fireRequestListenersOnForwards and ServletRequestListener is on Jetty

In reading up on Netty backend, instead of relying on RequestDispatcher, Netty uses a pipeline of handlers to process incoming requests. So whetever the 'listener' is just needs to be modified for handling back off to the handler pipeline

I am just not 100% on what the listener/ handler are to use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

3 participants