Skip to content

Commit 0b69a0b

Browse files
sdeleuzerstoyanchev
authored andcommitted
Add URI based MockHttpServletRequestBuilder constructors
This commit adds new MockHttpServletRequestBuilder constructors with an URI parameter in addition to the URL template + URL variables existing ones. It gives more control on how the URL is built, allowing for example to use URL variables containing '/' character with proper encoding. Issue: SPR-11441
1 parent c10eeb4 commit 0b69a0b

File tree

4 files changed

+243
-1
lines changed

4 files changed

+243
-1
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.test.web.servlet.request;
1818

1919
import java.io.UnsupportedEncodingException;
20+
import java.net.URI;
2021
import java.security.Principal;
2122
import java.util.ArrayList;
2223
import java.util.Arrays;
@@ -114,6 +115,7 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
114115
* <p>Although this class cannot be extended, additional ways to initialize
115116
* the {@code MockHttpServletRequest} can be plugged in via
116117
* {@link #with(RequestPostProcessor)}.
118+
* @param httpMethod the HTTP method (GET, POST, etc)
117119
* @param urlTemplate a URL template; the resulting URL will be encoded
118120
* @param urlVariables zero or more URL variables
119121
*/
@@ -124,6 +126,23 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
124126
this.uriComponents = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(urlVariables).encode();
125127
}
126128

129+
/**
130+
* Package private constructor. To get an instance, use static factory
131+
* methods in {@link MockMvcRequestBuilders}.
132+
* <p>Although this class cannot be extended, additional ways to initialize
133+
* the {@code MockHttpServletRequest} can be plugged in via
134+
* {@link #with(RequestPostProcessor)}.
135+
* @param httpMethod the HTTP method (GET, POST, etc)
136+
* @param url the URL
137+
* @since 4.0.3
138+
*/
139+
MockHttpServletRequestBuilder(HttpMethod httpMethod, URI url) {
140+
Assert.notNull(httpMethod, "httpMethod is required");
141+
Assert.notNull(url, "url is required");
142+
this.method = httpMethod;
143+
this.uriComponents = UriComponentsBuilder.fromUri(url).build();
144+
}
145+
127146
/**
128147
* Add a request parameter to the {@link MockHttpServletRequest}.
129148
* If called more than once, the new values are added.

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.test.web.servlet.request;
1818

19+
import java.net.URI;
1920
import java.util.ArrayList;
2021
import java.util.List;
2122
import javax.servlet.ServletContext;
@@ -52,6 +53,20 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque
5253
super.contentType(MediaType.MULTIPART_FORM_DATA);
5354
}
5455

56+
/**
57+
* Package-private constructor. Use static factory methods in
58+
* {@link MockMvcRequestBuilders}.
59+
* <p>For other ways to initialize a {@code MockMultipartHttpServletRequest},
60+
* see {@link #with(RequestPostProcessor)} and the
61+
* {@link RequestPostProcessor} extension point.
62+
* @param url the URL
63+
* @since 4.0.3
64+
*/
65+
MockMultipartHttpServletRequestBuilder(URI url) {
66+
super(HttpMethod.POST, url);
67+
super.contentType(MediaType.MULTIPART_FORM_DATA);
68+
}
69+
5570

5671
/**
5772
* Create a new MockMultipartFile with the given content.

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@
2222
import org.springframework.test.web.servlet.MvcResult;
2323
import org.springframework.test.web.servlet.RequestBuilder;
2424

25+
import java.net.URI;
26+
2527
/**
2628
* Static factory methods for {@link RequestBuilder}s.
2729
*
@@ -31,6 +33,7 @@
3133
* @author Arjen Poutsma
3234
* @author Rossen Stoyanchev
3335
* @author Greg Turnquist
36+
* @author Sebastien Deleuze
3437
* @since 3.2
3538
*/
3639
public abstract class MockMvcRequestBuilders {
@@ -109,6 +112,80 @@ public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTempla
109112
return new MockMultipartHttpServletRequestBuilder(urlTemplate, urlVariables);
110113
}
111114

115+
116+
/**
117+
* Create a {@link MockHttpServletRequestBuilder} for a GET request.
118+
* @param url the URL
119+
* @since 4.0.3
120+
*/
121+
public static MockHttpServletRequestBuilder get(URI url) {
122+
return new MockHttpServletRequestBuilder(HttpMethod.GET, url);
123+
}
124+
125+
/**
126+
* Create a {@link MockHttpServletRequestBuilder} for a POST request.
127+
* @param url the URL
128+
* @since 4.0.3
129+
*/
130+
public static MockHttpServletRequestBuilder post(URI url) {
131+
return new MockHttpServletRequestBuilder(HttpMethod.POST, url);
132+
}
133+
134+
/**
135+
* Create a {@link MockHttpServletRequestBuilder} for a PUT request.
136+
* @param url the URL
137+
* @since 4.0.3
138+
*/
139+
public static MockHttpServletRequestBuilder put(URI url) {
140+
return new MockHttpServletRequestBuilder(HttpMethod.PUT, url);
141+
}
142+
143+
/**
144+
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request.
145+
* @param url the URL
146+
* @since 4.0.3
147+
*/
148+
public static MockHttpServletRequestBuilder patch(URI url) {
149+
return new MockHttpServletRequestBuilder(HttpMethod.PATCH, url);
150+
}
151+
152+
/**
153+
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request.
154+
* @param url the URL
155+
* @since 4.0.3
156+
*/
157+
public static MockHttpServletRequestBuilder delete(URI url) {
158+
return new MockHttpServletRequestBuilder(HttpMethod.DELETE, url);
159+
}
160+
161+
/**
162+
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request.
163+
* @param url the URL
164+
* @since 4.0.3
165+
*/
166+
public static MockHttpServletRequestBuilder options(URI url) {
167+
return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, url);
168+
}
169+
170+
/**
171+
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method.
172+
* @param httpMethod the HTTP method (GET, POST, etc)
173+
* @param url the URL
174+
* @since 4.0.3
175+
*/
176+
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI url) {
177+
return new MockHttpServletRequestBuilder(httpMethod, url);
178+
}
179+
180+
/**
181+
* Create a {@link MockHttpServletRequestBuilder} for a multipart request.
182+
* @param url the URL
183+
* @since 4.0.3
184+
*/
185+
public static MockMultipartHttpServletRequestBuilder fileUpload(URI url) {
186+
return new MockMultipartHttpServletRequestBuilder(url);
187+
}
188+
112189
/**
113190
* Create a {@link RequestBuilder} for an async dispatch from the
114191
* {@link MvcResult} of the request that started async processing.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.web.servlet;
18+
19+
20+
import org.junit.Before;
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.springframework.beans.BeansException;
24+
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.beans.factory.config.BeanPostProcessor;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.core.PriorityOrdered;
29+
import org.springframework.stereotype.Component;
30+
import org.springframework.stereotype.Controller;
31+
import org.springframework.test.context.ContextConfiguration;
32+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
33+
import org.springframework.test.context.web.WebAppConfiguration;
34+
import org.springframework.ui.Model;
35+
import org.springframework.web.bind.annotation.PathVariable;
36+
import org.springframework.web.bind.annotation.RequestMapping;
37+
import org.springframework.web.bind.annotation.RequestMethod;
38+
39+
import org.springframework.web.context.WebApplicationContext;
40+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
41+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
42+
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
43+
import org.springframework.web.util.UriComponentsBuilder;
44+
45+
import java.net.URI;
46+
47+
import static org.hamcrest.core.Is.is;
48+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
49+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
50+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
51+
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
52+
53+
/**
54+
* Tests for SPR-11441 (MockMvc needs to accept prepared URI with encoded URI path variables).
55+
*
56+
* @author Sebastien Deleuze
57+
*/
58+
@RunWith(SpringJUnit4ClassRunner.class)
59+
@WebAppConfiguration
60+
@ContextConfiguration
61+
public class Spr11441Tests {
62+
63+
@Autowired
64+
private WebApplicationContext wac;
65+
66+
private MockMvc mockMvc;
67+
68+
@Before
69+
public void setup() {
70+
this.mockMvc = webAppContextSetup(this.wac).build();
71+
}
72+
73+
@Test
74+
public void test() throws Exception {
75+
String id = "a/b";
76+
URI url = UriComponentsBuilder.fromUriString("/circuit").pathSegment(id).build().encode().toUri();
77+
ResultActions result = mockMvc.perform(get(url));
78+
result.andExpect(status().isOk()).andExpect(model().attribute("receivedId", is(id)));
79+
}
80+
81+
82+
@Configuration
83+
@EnableWebMvc
84+
static class WebConfig extends WebMvcConfigurerAdapter {
85+
86+
@Bean
87+
public MyController myController() {
88+
return new MyController();
89+
}
90+
91+
@Bean
92+
public HandlerMappingConfigurer myHandlerMappingConfigurer() {
93+
return new HandlerMappingConfigurer();
94+
}
95+
}
96+
97+
@Controller
98+
private static class MyController {
99+
100+
@RequestMapping(value = "/circuit/{id}", method = RequestMethod.GET)
101+
public String getCircuit(@PathVariable String id, Model model) {
102+
model.addAttribute("receivedId", id);
103+
return "result";
104+
}
105+
}
106+
107+
@Component
108+
private static class HandlerMappingConfigurer implements BeanPostProcessor, PriorityOrdered {
109+
110+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
111+
if (bean instanceof RequestMappingHandlerMapping) {
112+
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) bean;
113+
114+
// URL decode after request mapping, not before.
115+
requestMappingHandlerMapping.setUrlDecode(false);
116+
117+
}
118+
119+
return bean;
120+
}
121+
122+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
123+
return bean;
124+
}
125+
126+
public int getOrder() {
127+
return PriorityOrdered.HIGHEST_PRECEDENCE;
128+
}
129+
}
130+
131+
}

0 commit comments

Comments
 (0)