Details
Description
Good Afternoon,
I have a controller with the following mapping
@RequestMapping(value = "/circuit/{id}/view", method = RequestMethod.GET) public String getCircuit(@PathVariable String id, @ModelAttribute("search") CircuitSearchController.SearchParams search, Model model, Principal principal) {...}
The id value can contain reserved characters so I URL encode when rendering the view. For an id of "ja-ran-17 gigabitethernet 10/0.5790740:579-747" the browser request thus looks like
http://localhost/myapp/circuit/ja-ran-17%20gigabitethernet%2010%2F0.5790740:579-747/view
This is working ok.
When i try to write a test for it however, the URL is getting double encoded
ResultActions result = mockMvc.perform(get("/circuit/{id}/view", "ja-ran-17%20gigabitethernet%2010%2F0.5790740:579-747").principal(p)); // THEN - circuit should be in the model result.andExpect(status().isOk()).andExpect(model().attribute("circuit", hasProperty("circuitId", is(id))));
log from real request in tomcat
DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcherServlet' processing GET request for [/circuit-id-manager/circuit/ja-ran-17 gigabitethernet 10/0.5790740:579-747/view] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /circuit/ja-ran-17%20gigabitethernet%2010%2F0.5790740:579-747/view DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public java.lang.String [...]
log from mockmvc request
DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing GET request for [/circuit/ja-ran-47%20gigabitethernet%2010%2F0.5790740:579-747/view] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /circuit/ja-ran-47%2520gigabitethernet%252010%252F0.5790740:579-747/view
With a real request my controller gets passed id="ja-ran-17 gigabitethernet 10/0.5790740:579-747"
With a mock request my controller is passed id="ja-ran-47%20gigabitethernet%2010%2F0.5790740:579-747"
Perhaps it is a feature of MockMvc that it automatically does URI encoding of path variables? If so it is inconsistent with other parts of the Web-Mvc framework see SPR-11401
So i remove the encoding of my path variable and let MockMvc encode it. Now it doesn't find the handler
DEBUG o.s.t.w.s.TestDispatcherServlet - DispatcherServlet with name '' processing GET request for [/circuit/ja-ran-47 gigabitethernet 10/0.5790740:579-747/view] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /circuit/ja-ran-47%20gigabitethernet%2010/0.5790740:579-747/view DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/circuit/ja-ran-47%20gigabitethernet%2010/0.5790740:579-747/view]
I had a similar problem where the framework was treating %2F as / in contravention of RFC3986[1]. That is solved by a BeanPostProcessor calling
// URL decode after request mapping, not before. requestMappingHandlerMapping.setUrlDecode(false); // Workaround to make the previous fix work. See https://jira.springsource.org/browse/SPR-11101. requestMappingHandlerMapping.setAlwaysUseFullPath(true);
My guess is that the MockMvc Framework is not using the normal request mapping handler mapping, and so calling setUrlDecode on the normal one has no effect?
[1]"When a URI is dereferenced, the components and subcomponents
significant to the scheme-specific dereferencing process (if any)
must be parsed and separated before the percent-encoded octets within
those components can be safely decoded, as otherwise the data may be
mistaken for component delimiters."
and again in 7.3
"Percent-encoded octets must be decoded at some point during the
dereference process. Applications must split the URI into its
components and subcomponents prior to decoding the octets, as
otherwise the decoded octets might be mistaken for delimiters."
That is clearly what is happening here, %2F is being mistaken for a path delimiter. Path delimitation must take place before decoding.