2525import java .util .function .Consumer ;
2626
2727import org .springframework .lang .Nullable ;
28+ import org .springframework .util .Assert ;
2829import org .springframework .util .MultiValueMap ;
2930import org .springframework .util .ObjectUtils ;
31+ import org .springframework .web .util .DefaultUriBuilderFactory ;
32+ import org .springframework .web .util .UriTemplateHandler ;
3033
3134/**
3235 * Extension of {@link HttpEntity} that adds a {@linkplain HttpMethod method} and
33- * {@linkplain URI uri}. Used in {@code RestTemplate} and {@code @Controller} methods.
36+ * {@linkplain URI uri} or String based uri template with placeholder. Uri variables can be provided
37+ * through the builder {@link HeadersBuilder#uriVariables(Object...)}.
38+ *
39+ * Used in {@code RestTemplate} and {@code @Controller} methods.
3440 *
3541 * <p>In {@code RestTemplate}, this class is used as parameter in
3642 * {@link org.springframework.web.client.RestTemplate#exchange(RequestEntity, Class) exchange()}:
5460 * RequestEntity<MyRequest> request = RequestEntity.post(uri).accept(MediaType.APPLICATION_JSON).body(body);
5561 * </pre>
5662 *
63+ * <p> if you would like to provide a string base URI template with variable, consider using
64+ * {@link RequestEntity#method(HttpMethod, String)}}, {@link RequestEntity#get(String)},
65+ * {@link RequestEntity#post(String)}, {@link RequestEntity#put(String)}, {@link RequestEntity#delete(String)},
66+ * {@link RequestEntity#patch(String)}, {@link RequestEntity#options(String)}
67+ *
68+ * <pre class="code>
69+ * RequestEntity request = RequestEntity
70+ * .post("https://example.com/{foo}")
71+ * .uriVariables("bar")
72+ * .accept(MediaType.APPLICATION_JSON)
73+ * .build();
74+ * </pre>
75+ *
5776 * <p>Can also be used in Spring MVC, as a parameter in a @Controller method:
5877 * <pre class="code">
5978 * @RequestMapping("/handle")
6685 *
6786 * @author Arjen Poutsma
6887 * @author Sebastien Deleuze
88+ * @author Parviz Rozikov
6989 * @since 4.1
7090 * @param <T> the body type
7191 * @see #getMethod()
7292 * @see #getUrl()
7393 */
7494public class RequestEntity <T > extends HttpEntity <T > {
7595
96+ private final static UriTemplateHandler DEFAULT_URI_BUILDER_FACTORY = new DefaultUriBuilderFactory ();
97+
7698 @ Nullable
7799 private final HttpMethod method ;
78100
101+ @ Nullable
79102 private final URI url ;
80103
104+ @ Nullable
105+ private String uri ;
106+
107+ @ Nullable
108+ private Object [] uriVariables ;
109+
81110 @ Nullable
82111 private final Type type ;
83112
@@ -146,14 +175,32 @@ public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> h
146175 * @since 4.3
147176 */
148177 public RequestEntity (@ Nullable T body , @ Nullable MultiValueMap <String , String > headers ,
149- @ Nullable HttpMethod method , URI url , @ Nullable Type type ) {
178+ @ Nullable HttpMethod method , URI url , @ Nullable Type type ) {
150179
151180 super (body , headers );
152181 this .method = method ;
153182 this .url = url ;
154183 this .type = type ;
155184 }
156185
186+ /**
187+ * Private Constructor with method, URL, UriTemplate and varargs urivariables but without body nor headers.
188+ * @param method the method
189+ * @param url the URL
190+ * @param uri the UriTemplate
191+ * @param uriVariables the uriVariables
192+ */
193+ private RequestEntity (MultiValueMap <String , String > headers , HttpMethod method , @ Nullable URI url ,
194+ @ Nullable String uri , @ Nullable Object ... uriVariables ) {
195+ super (null , headers );
196+ Assert .isTrue (uri == null || url == null , "Either url or url must be not null" );
197+ this .method = method ;
198+ this .url = url ;
199+ this .type = null ;
200+ this .uri = uri ;
201+ this .uriVariables = uriVariables ;
202+ }
203+
157204
158205 /**
159206 * Return the HTTP method of the request.
@@ -166,12 +213,30 @@ public HttpMethod getMethod() {
166213
167214 /**
168215 * Return the URL of the request.
216+ * Used {@link org.springframework.web.util.DefaultUriBuilderFactory} to expand and
217+ * encode {@link DefaultUriBuilderFactory#setEncodingMode} when provided {@link RequestEntity#uri}
169218 * @return the URL as a {@code URI}
170219 */
171220 public URI getUrl () {
172- return this .url ;
221+ if (uri == null ) {
222+ return this .url ;
223+ }
224+ return DEFAULT_URI_BUILDER_FACTORY .expand (uri , uriVariables );
225+ }
226+
227+ /**
228+ * Return the URL of the request.
229+ * @return the URL as a {@code URI}
230+ * @since 5.3
231+ */
232+ public URI getUrl (UriTemplateHandler uriTemplateHandler ) {
233+ if (uri == null ) {
234+ return this .url ;
235+ }
236+ return uriTemplateHandler .expand (uri , uriVariables );
173237 }
174238
239+
175240 /**
176241 * Return the type of the request's body.
177242 * @return the request's body type, or {@code null} if not known
@@ -241,6 +306,19 @@ public static BodyBuilder method(HttpMethod method, URI url) {
241306 return new DefaultBodyBuilder (method , url );
242307 }
243308
309+ /**
310+ * Create a builder with the given method and given string base uri template.
311+ * @param method the HTTP method (GET, POST, etc)
312+ * @param uri the uri
313+ * @return the created builder
314+ * @see RequestEntity
315+ * @since 5.3
316+ */
317+ public static BodyBuilder method (HttpMethod method , String uri ) {
318+ return new DefaultBodyBuilder (method , uri );
319+ }
320+
321+
244322 /**
245323 * Create an HTTP GET builder with the given url.
246324 * @param url the URL
@@ -250,6 +328,16 @@ public static HeadersBuilder<?> get(URI url) {
250328 return method (HttpMethod .GET , url );
251329 }
252330
331+ /**
332+ * Create an HTTP GET builder with the given string base uri template.
333+ * @param uri the uri template
334+ * @return the created builder
335+ * @since 5.3
336+ */
337+ public static HeadersBuilder <?> get (String uri ) {
338+ return method (HttpMethod .GET , uri );
339+ }
340+
253341 /**
254342 * Create an HTTP HEAD builder with the given url.
255343 * @param url the URL
@@ -259,6 +347,16 @@ public static HeadersBuilder<?> head(URI url) {
259347 return method (HttpMethod .HEAD , url );
260348 }
261349
350+ /**
351+ * Create an HTTP HEAD builder with the given string base uri template.
352+ * @param uri the uri template
353+ * @return the created builder
354+ * @since 5.3
355+ */
356+ public static HeadersBuilder <?> head (String uri ) {
357+ return method (HttpMethod .HEAD , uri );
358+ }
359+
262360 /**
263361 * Create an HTTP POST builder with the given url.
264362 * @param url the URL
@@ -268,6 +366,16 @@ public static BodyBuilder post(URI url) {
268366 return method (HttpMethod .POST , url );
269367 }
270368
369+ /**
370+ * Create an HTTP POST builder with the given string base uri template.
371+ * @param uri the uri template
372+ * @return the created builder
373+ * @since 5.3
374+ */
375+ public static BodyBuilder post (String uri ) {
376+ return method (HttpMethod .POST , uri );
377+ }
378+
271379 /**
272380 * Create an HTTP PUT builder with the given url.
273381 * @param url the URL
@@ -277,6 +385,16 @@ public static BodyBuilder put(URI url) {
277385 return method (HttpMethod .PUT , url );
278386 }
279387
388+ /**
389+ * Create an HTTP PUT builder with the given string base uri template.
390+ * @param uri the uri template
391+ * @return the created builder
392+ * @since 5.3
393+ */
394+ public static BodyBuilder put (String uri ) {
395+ return method (HttpMethod .PUT , uri );
396+ }
397+
280398 /**
281399 * Create an HTTP PATCH builder with the given url.
282400 * @param url the URL
@@ -286,6 +404,16 @@ public static BodyBuilder patch(URI url) {
286404 return method (HttpMethod .PATCH , url );
287405 }
288406
407+ /**
408+ * Create an HTTP PATCH builder with the given string base uri template.
409+ * @param uri the uri template
410+ * @return the created builder
411+ * @since 5.3
412+ */
413+ public static BodyBuilder patch (String uri ) {
414+ return method (HttpMethod .PATCH , uri );
415+ }
416+
289417 /**
290418 * Create an HTTP DELETE builder with the given url.
291419 * @param url the URL
@@ -295,6 +423,16 @@ public static HeadersBuilder<?> delete(URI url) {
295423 return method (HttpMethod .DELETE , url );
296424 }
297425
426+ /**
427+ * Create an HTTP DELETE builder with the given string base uri template.
428+ * @param uri the uri template
429+ * @return the created builder
430+ * @since 5.3
431+ */
432+ public static HeadersBuilder <?> delete (String uri ) {
433+ return method (HttpMethod .DELETE , uri );
434+ }
435+
298436 /**
299437 * Creates an HTTP OPTIONS builder with the given url.
300438 * @param url the URL
@@ -304,6 +442,16 @@ public static HeadersBuilder<?> options(URI url) {
304442 return method (HttpMethod .OPTIONS , url );
305443 }
306444
445+ /**
446+ * Creates an HTTP OPTIONS builder with the given string base uri template.
447+ * @param uri the uri template
448+ * @return the created builder
449+ * @since 5.3
450+ */
451+ public static HeadersBuilder <?> options (String uri ) {
452+ return method (HttpMethod .OPTIONS , uri );
453+ }
454+
307455
308456 /**
309457 * Defines a builder that adds headers to the request entity.
@@ -383,6 +531,13 @@ public interface HeadersBuilder<B extends HeadersBuilder<B>> {
383531 */
384532 B ifNoneMatch (String ... ifNoneMatches );
385533
534+ /**
535+ * Set the values of the {@code If-None-Match} header.
536+ * @param uriVariables the variables to expand the template
537+ * @since 5.3
538+ */
539+ B uriVariables (Object ... uriVariables );
540+
386541 /**
387542 * Builds the request entity with no body.
388543 * @return the request entity
@@ -439,13 +594,28 @@ private static class DefaultBodyBuilder implements BodyBuilder {
439594
440595 private final HttpMethod method ;
441596
597+ @ Nullable
442598 private final URI url ;
443599
600+ @ Nullable
601+ private final String uri ;
602+
603+ @ Nullable
604+ private Object [] uriVariables ;
605+
606+
444607 private final HttpHeaders headers = new HttpHeaders ();
445608
446609 public DefaultBodyBuilder (HttpMethod method , URI url ) {
447610 this .method = method ;
448611 this .url = url ;
612+ this .uri = null ;
613+ }
614+
615+ public DefaultBodyBuilder (HttpMethod method , String uri ) {
616+ this .method = method ;
617+ this .uri = uri ;
618+ this .url = null ;
449619 }
450620
451621 @ Override
@@ -518,9 +688,18 @@ public BodyBuilder ifNoneMatch(String... ifNoneMatches) {
518688 return this ;
519689 }
520690
691+ @ Override
692+ public BodyBuilder uriVariables (Object ... uriVariables ) {
693+ this .uriVariables = uriVariables ;
694+ return this ;
695+ }
696+
521697 @ Override
522698 public RequestEntity <Void > build () {
523- return new RequestEntity <>(this .headers , this .method , this .url );
699+ if (this .url != null ){
700+ new RequestEntity <>(this .headers , this .method , this .url );
701+ }
702+ return new RequestEntity <>(this .headers , this .method , this .url , uri , uriVariables );
524703 }
525704
526705 @ Override
0 commit comments