Skip to content

MockMvc needs to accept prepared URI with encoded URI path variables [SPR-11441] #16067

@spring-projects-issues

Description

@spring-projects-issues

daniel carter opened SPR-11441 and commented

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 #16028

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.


Affects: 3.2.5

Attachments:

Referenced from: commits 0b69a0b

0 votes, 5 watchers

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions