I'm working on a Spring MVC project using Annotated Controller.
One thing that I'm interested in is about the order which #RequestMapping instruction to be processed.
For example, I want all /green/basic/welcome to be mapped to GreenController.welcome()
but green/{treeId}/{treeName} to be mapped to GreenController.viewTree(treeId, treeName).
I guess I need to specify two #RequestMapping with #RequestMapping of /green/basic/welcome to be processed first, so that it won't be interpreter as a call to GreenControllerviewTree("basic", "welcome").
Can you guys guide me on that?
An exact match for a RequestMapping will take precedence over one with a PathVariable. So you would have two request mappings like you pointed out. One to handle the specific url, and the variable version will catch everything else. Spring checks for direct path matches before checking for path variable matches, so order does not matter unless you have two request mappings with the same number of path variables, which may spit out an IllegalStateException
Check the source of org.springframework.web.servlet.handler.AbstractHandlerMethodMapping for the specifics. It is handled in lookupHandlerMethod().
To determine the best match of two RequestMappings that aren't exact matches, the compareTo() method of RequestMappingInfo is used.
Related
I am struggling to understand the differences between the attribute name, path and value in #RequestMapping. I looked into the API, but it doesn't illustrate it clearly. I googled for hours but it just works partially. Can you tell me the details? Thank you!
Let's see the javadoc. Start with path:
The path mapping URIs (e.g. "/profile").
So that's the path(s) that will be used by Spring to decide if this methd should be called or not, based on the actual path of the request.
Now value:
The primary mapping expressed by this annotation.
This is an alias for path(). For example, #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
So, it's quite clear: value and path are exactly equivalent.
And finally name:
Assign a name to this mapping.
So this is completely different. It gives a name to this mapping. Why could a name be useful for a mapping? You can discover that by clicking on the "See also" classes linked to attrbibute in the documentation. The javadoc for HandlerMethodMappingNamingStrategy says:
Applications can build a URL to a controller method by name with the help of the static method MvcUriComponentsBuilder#fromMappingName [...]
So, as you see, the name of a mapping can be used to construct a URL that will match to a given controller method by using the name of its mapping. That makes it possible to change the path of a mapping without changing it everywhere, by using names rather than paths.
The reference documentation, in addition to the javadoc, should be your first destination. This is usually way more effective when you want to learn about the framework than googling.
I've made a Gist to try to illustrate the issue I'm trying to solve:
https://gist.github.com/jmcgill-public/e05d4378049296f05691
I have a Spring #Controller with a #RequestMapping value, and a #WebFilter with a urlPattern to match. The Filter initializes, but is not executed on the corresponding Controller method.
If I change the Filter's urlPattern to "/*", the Filter does execute (for every request).
Why doesn't it execute for these matching urls?
Answering my own question, if that's okay.
The answer is, the urlPattern in a #WebFilter must be the request URL according to HTTP, meaning it must include any context path preceding the #Controller's #RequestMapping path.
This is inconvenient, but I didn't understand that #WebFilters are javax.servlet.annotations, not org.springframework annotations. So I was mixing up layers and mixing framework technologies.
I have been developing with Spring MVC in a Servlet environment for quite some time, mostly on Spring 3.1.x and 3.2.x, but recently moved jobs to a company that uses a mixture of Portlets and Servlets using Spring 3.0.6.
Fist, there seems to be two versions of DefaultAnnotationHandlerMapping, one for Portlets and one for Servlets. My limited knowledge of Portlets makes me believe they are just an extention of Servlets, so I can vaguely understand the need to have two separate HandlerMappings, one to handle generic Servlets, the other to handle specifics to Portlest. What is throwing me is how they each seems to behave differently with regaurds to how they handle the #RequestMapping annotations, and how my new employer has chosen to set things up.
In my past, I have had one DispatchHandler for the entire application. I know you can have multiple DispatchHandlers, but I never saw the benefit past segregation of sub-context XMLs. Anyhow, in the past I would have a DispatchHandler mapped to "/", and then each Controller set a mapping on top using #RequestMapping("/someUrl") and then another #RequestMapping on each handler method further extending the URL mapping. So, from a browser I would get something like:
http://somehost.com/myWar/myController/myMethod
where the first "/myWar" is mapped to the WAR deployment, the next "/" is mapped to the DispatchServlet, "/myController" is the Controller's mapping, and anything after is matched to some method using a combination of URL signature, request method, parameters, etc.
Now lets evaluate what I am seeing at my new firm. First, each Controller gets its own DispatchServlet, so there is like 20-30 DispatchServlets defined, all with different base URIs mapped, some with the same base Application Context XML, some with different ones, all using the same Application Context from the ContextLoaderListener. Each of the subsequent DispatchServlet's path is the same as the #RequestMapping on their "mapped" Controller, so in the above example DispatchServlet would have the path "/myController" and the Controller would have #RequestMapping("/myController"). On top of that, at the Controller's method level, the value specified on #RequestMapping seems to totally ignore any additional pathing applied to it.
So, for instance, if I have a Controller with #RequestMapping("/myController"), followed by a method with #RequestMapping("/edit"), hitting the URL http://somehost.com/myWar/myContoller/edit gives me a 404. However, if I change the method-level #RequestMapping to #RequestMapping(parameter="actionMethod=edit"), and the URL to http://somehost.com/myWar/myController?actionMethod=edit, it maps just fine and I get my page. Further, if I change my method-level #RequestMapping to #RequestMapping(value="/edit",params="actionMethod=view") neither http://somehost.com/myWar/myController?actionMethod=edit nor http://somehost.com/myWar/myController/edit?actionMethod=edit work at all. The problem is that now to get to anything besides the default mapping at the method level requires me to take on "?actionMethod=edit", instead of simply extending the URI with the added paths. Also, it prevents me from using a RESTful approach, since now I can't define something like #RequestMapping("/somePath/{id}").
I have so much confussion in my brain, like why is the DispatchServlet mapping in the web.xml overlapping the Controllers #RequestMapping, and why is the #RequestMapping at the Controller's method level not working when I set the value property to a valid URI mapping, but works fine when I set some parameter mapping?
Oh, and sorry for the long posting. I just want to get a better understanding of what is happening here so I can either deal with it with an intellegent approach, or go back to my new group and say "this should be changed, and this is why". I can extend this lengthy question with some simplified code examples if that helps.
I'm running Spring MVC 3.x and I have one Controller with RequestMapping annotations (DefaultAnnotationHandlerMapping) and I have one ServletForwardingController with some additional mappings via SimpleUrlHandlerMapping. When I boot up my application, I see the following:
...
13:24:17,747 INFO [DefaultAnnotationHandlerMapping] Mapped URL path [/{query}] onto handler [com.foo.controllers.BarController#1b20a36]
13:24:17,997 INFO [SimpleUrlHandlerMapping] Root mapping to handler [org.springframework.web.servlet.mvc.ParameterizableViewController#598535]
13:24:18,044 INFO [SimpleUrlHandlerMapping] Mapped URL path [/spring*.ftl] onto handler [org.springframework.web.servlet.mvc.ServletForwardingController#56eb62]
13:24:18,044 INFO [SimpleUrlHandlerMapping] Mapped URL path [/shared-resources/**] onto handler [org.springframework.web.servlet.mvc.ServletForwardingController#56eb62]
...
My BarController is of course catching all requests (like /spring_en_US.ftl), but I want it to be tried last. In other words, I want the SimpleUrlHandlerMapping to take priority over the DefaultAnnotationHandlerMappings in my application.
The best solution I found was to replace mvc:annotation-config with explicit DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter bean configuration. I was then able to set the order property on both SimpleUrlHandlerMapping (order = 0) and DefaultAnnotationHandlerMapping (order = 1). This set the priority of the handlers as I needed.
I tried to reorder mvc:annotation-config below SimpleUrlHandlerMapping, but it didn't change the handler priority as needed.
DispatcherServlet should consult the various HandlerMapping beans in the order in which they're declared in the beans file. The first one that says "yes, I have a mapping for that" gets the prize.
If you're manually declaring a SimpleUrlHandlerMapping and DefaultAnnotationHandlerMapping, then make sure they're in the right order.
If you're not declaring them yourself, then Spring will use the defaults, which is a BeanNameUrlHandlerMapping followed by a DefaultAnnotationHandlerMapping.
I recently want to have a special routing rule : {*whatever}/details/{Id}/{itemName}
I know an exception will be thrown once I run the application. In my application, for example my url pattern is www.domain.com/root/parent/child/.../child/details/30/itemname
but the current routing doesnot support this. How can custom the routing handler to make it work?
A class has been written that supports this
I've written such a class that can handle catch-all segment anywhere in the URL. There's quite some code to it, but it works as expected and I've used it on a real life project.
Check it out yourself and see if it fulfils your needs.
The problem is... how will it know when to stop?
the {*whatever} segment will match:
/foo/
/foo/bar
/foo/bar/details/4/moreFoo
/foo/bar/andmore/details/4/moreFoo
Because the catch-all parameter includes anything, it will never stop.
The only way to implement this would be to create a different route for each place you use details...
eg:
games/details/{id}/{itemName}
widgets/details/{id}/{itemName}
books/details/{id}/{itemName}
Of course, that is already provided in the default {controller}/{action}/{id} route
I think you may want to look at extending the System.Web.Routing.RouteBase class and override the GetRouteData() method. With it you can look at the requested url and decide if matches your pattern and if so construct and return a new instance of RouteData that points to the controller and action that you want to handle the request. Otherwise if you don't match the requested url you return null.
See the following for examples:
Pro ASP.NET MVC Framework
By Steve Sanderson
Custom RouteBase