resthub / springmvc-router

Adds route mapping capacity to any "Spring MVC based" webapp. Uses playframework.org Router implementation.
http://resthub.github.com/springmvc-router/
Other
167 stars 63 forks source link

Remove need to configure servlet prefix #22

Closed sazzer closed 11 years ago

sazzer commented 11 years ago

Currently, the router only works if it is configured with the servlet prefix for the servlet being routed. This means that the router needs to know the servlet prefix from web.xml AND the name of the webapp in the container. Given that the name of the webapp is a deployment-time concern, this ties the deployment to code inside of the War.

It would be useful if there was some way to dynamically work out these details at request time instead, based on the HttpServletRequest coming in. I suspect that in 90+% of the cases, the value to use will get request.getContextPath() + request.getServletPath() - which gives you the name of the webapp deployed in the container plus the name of the servlet inside of the container.

bclozel commented 11 years ago

So in other words, the router could:

I think SpringMVC actually discards the context+servlet paths before routing using the RequestMapping annotations. But on the reverse part, ServletUriComponentsBuilder needs the current HTTPServletRequest to get this information based on the current request.

Problems are:

What do you think?

Most of the time (always?), the DispatcherServlet is bound to only one context+servlet path at startup. So you could use maven filtering and/or Spring dynamic properties to do this part?

sazzer commented 11 years ago

The servlet part isn't especially dynamic - it's configured in web.xml - but it's configuration that is outside the scope of the code. The context part is more dynamic because that's down to how the webapp is installed in the container. The code itself shouldn't have any preconceptions on either of those values - especially since it can quite easily have the exact same class instance running in two contexts or two servlets at the exact same time. Equally, the host, subdomain, port and other things are all things that are configured outside of the codebase, and whats more things that may be non-unique. I can quite easily have a webapp hosted in Tomcat, mounted on different contexts on different hosts and ports at the same time. In that situation, what configuration do you use for the router.reverse call?

Just because "most of the time" apps are deployed that way, doesn't mean "always", and certainly doesn't mean you should restrict the code to only work that way...

bclozel commented 11 years ago

Routing requests -> Controller actions

A new configuration item is needed to say wether or not the context and servlet paths are implied during the routing phase (i.e. stripped before route matching). In that case, different requests ("/context/servlet/mypath" and "context2/servlet2/mypath") could match the same controller. Is that a problem?

Reverse routing

A new reverse method could be added on the Router, taking an ServletHTTPRequest as a parameter, in order to extract those paths from the current request. If the reverse routing is done at the Controller level it's fine, but if you want to integrate the reverse routing within the view, you'd have to explicitely put the current request in the ModelAndView object (I'm not a big fan of ThreadLocal...). I can see a problem with separation of concerns here.

bclozel commented 11 years ago

In that situation, what configuration do you use for the router.reverse call?

When reverse-routing a Controller.action, you can get a path (thus letting the browser prepend the current domain) or give the host as a parameter.

sazzer commented 11 years ago

Routing requests -> Controller actions

If you have the same Spring context with the same configuration loaded into two different servlet paths, I would expect it to work exactly as you described. If you have different Spring contexts with different configurations then you could configure them differently easily enough.

Reverse routing

This is an ideal situation for a ThreadLocal I would have thought. I've done this in the past using an Interceptor to wrap the request so that the Interceptor puts the Request into a ThreadLocal automatically and makes it available - so that the caller can't see any of this logic themselves. They then just call the methods on the provided URL Writers and it does it all for them automatically.

I've actually written in the past such logic as this where you pass in a Controller class, Method name and a map of parameters and it works everything out for you automatically. Quite nice to use, though a bit hairy to write and get exactly right! It essentially means that you are generating URLs to a particular controller method, and the code only needs to know the structure of the code, not the structure of the configured URL routing...