resthub / springmvc-router

Adds route mapping capacity to any "Spring MVC based" webapp. Uses playframework.org Router implementation.

Home Page:http://resthub.github.com/springmvc-router/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Remove need to configure servlet prefix

sazzer opened this issue · comments

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.

So in other words, the router could:

  • automatically remove the context + servlet parts from the incoming request, and route only on that part.
  • automatically re-add context + servlet path info when doing the reverse routing

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:

  • when doing such thing, reverse routing gets harder (and SpringMVC didn't have reverse routing before 3.1 version)
  • I think static-based routing/reverse routing is incompatible with this
  • If the context+servlet parts really are dynamic, then the developer should give them as parameters to the router.reverse call, just like the host/subdomain routing feature.

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?

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...

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.

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.

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...