Skip to content

Commit da36169

Browse files
committed
Add MediaType parameter to ApiVersionInserter
Closes gh-35259
1 parent 08ccf46 commit da36169

File tree

4 files changed

+65
-28
lines changed

4 files changed

+65
-28
lines changed

spring-web/src/main/java/org/springframework/web/client/ApiVersionInserter.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,30 +57,38 @@ default void insertVersion(Object version, HttpHeaders headers) {
5757
* @param header the name of a header to hold the version
5858
*/
5959
static ApiVersionInserter useHeader(@Nullable String header) {
60-
return new DefaultApiVersionInserterBuilder(header, null, null).build();
60+
return new DefaultApiVersionInserterBuilder(header, null, null, null).build();
6161
}
6262

6363
/**
6464
* Create an inserter that sets a query parameter.
6565
* @param queryParam the name of a query parameter to hold the version
6666
*/
6767
static ApiVersionInserter useQueryParam(@Nullable String queryParam) {
68-
return new DefaultApiVersionInserterBuilder(null, queryParam, null).build();
68+
return new DefaultApiVersionInserterBuilder(null, queryParam, null, null).build();
69+
}
70+
71+
/**
72+
* Create an inserter to set a MediaType parameter on the "Content-Type" header.
73+
* @param mediaTypeParam the name of the media type parameter to hold the version
74+
*/
75+
static ApiVersionInserter useMediaTypeParam(@Nullable String mediaTypeParam) {
76+
return new DefaultApiVersionInserterBuilder(null, null, mediaTypeParam, null).build();
6977
}
7078

7179
/**
7280
* Create an inserter that inserts a path segment.
7381
* @param pathSegmentIndex the index of the path segment to hold the version
7482
*/
7583
static ApiVersionInserter usePathSegment(@Nullable Integer pathSegmentIndex) {
76-
return new DefaultApiVersionInserterBuilder(null, null, pathSegmentIndex).build();
84+
return new DefaultApiVersionInserterBuilder(null, null, null, pathSegmentIndex).build();
7785
}
7886

7987
/**
8088
* Create a builder for an {@link ApiVersionInserter}.
8189
*/
8290
static Builder builder() {
83-
return new DefaultApiVersionInserterBuilder(null, null, null);
91+
return new DefaultApiVersionInserterBuilder(null, null, null, null);
8492
}
8593

8694

@@ -101,6 +109,12 @@ interface Builder {
101109
*/
102110
Builder useQueryParam(@Nullable String queryParam);
103111

112+
/**
113+
* Create an inserter to set a MediaType parameter on the "Content-Type" header.
114+
* @param param the name of the media type parameter to hold the version
115+
*/
116+
Builder useMediaTypeParam(@Nullable String param);
117+
104118
/**
105119
* Configure the inserter to insert a path segment.
106120
* @param pathSegmentIndex the index of the path segment to hold the version

spring-web/src/main/java/org/springframework/web/client/DefaultApiVersionInserter.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
import java.net.URI;
2020
import java.util.ArrayList;
21+
import java.util.LinkedHashMap;
2122
import java.util.List;
23+
import java.util.Map;
2224

2325
import org.jspecify.annotations.Nullable;
2426

2527
import org.springframework.http.HttpHeaders;
28+
import org.springframework.http.MediaType;
2629
import org.springframework.util.Assert;
2730
import org.springframework.web.util.UriComponentsBuilder;
2831

@@ -39,20 +42,23 @@ final class DefaultApiVersionInserter implements ApiVersionInserter {
3942

4043
private final @Nullable String queryParam;
4144

45+
private final @Nullable String mediaTypeParam;
46+
4247
private final @Nullable Integer pathSegmentIndex;
4348

4449
private final ApiVersionFormatter versionFormatter;
4550

4651

4752
DefaultApiVersionInserter(
48-
@Nullable String header, @Nullable String queryParam, @Nullable Integer pathSegmentIndex,
49-
@Nullable ApiVersionFormatter formatter) {
53+
@Nullable String header, @Nullable String queryParam, @Nullable String mediaTypeParam,
54+
@Nullable Integer pathSegmentIndex, @Nullable ApiVersionFormatter formatter) {
5055

51-
Assert.isTrue(header != null || queryParam != null || pathSegmentIndex != null,
52-
"Expected 'header', 'queryParam', or 'pathSegmentIndex' to be configured");
56+
Assert.isTrue(header != null || queryParam != null || mediaTypeParam != null || pathSegmentIndex != null,
57+
"Expected 'header', 'queryParam', 'mediaTypeParam', or 'pathSegmentIndex' to be configured");
5358

5459
this.header = header;
5560
this.queryParam = queryParam;
61+
this.mediaTypeParam = mediaTypeParam;
5662
this.pathSegmentIndex = pathSegmentIndex;
5763
this.versionFormatter = (formatter != null ? formatter : Object::toString);
5864
}
@@ -86,7 +92,17 @@ private void assertPathSegmentIndex(Integer index, int pathSegmentsSize, URI uri
8692
@Override
8793
public void insertVersion(Object version, HttpHeaders headers) {
8894
if (this.header != null) {
89-
headers.set(this.header, this.versionFormatter.formatVersion(version));
95+
String formattedVersion = this.versionFormatter.formatVersion(version);
96+
headers.set(this.header, formattedVersion);
97+
}
98+
if (this.mediaTypeParam != null) {
99+
MediaType contentType = headers.getContentType();
100+
if (contentType != null) {
101+
Map<String, String> params = new LinkedHashMap<>(contentType.getParameters());
102+
params.put(this.mediaTypeParam, this.versionFormatter.formatVersion(version));
103+
contentType = new MediaType(contentType, params);
104+
headers.setContentType(contentType);
105+
}
90106
}
91107
}
92108

spring-web/src/main/java/org/springframework/web/client/DefaultApiVersionInserterBuilder.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,62 +33,61 @@ final class DefaultApiVersionInserterBuilder implements ApiVersionInserter.Build
3333

3434
private @Nullable String queryParam;
3535

36+
private @Nullable String mediaTypeParam;
37+
3638
private @Nullable Integer pathSegmentIndex;
3739

3840
private @Nullable ApiVersionFormatter versionFormatter;
3941

4042

4143
DefaultApiVersionInserterBuilder(
42-
@Nullable String header, @Nullable String queryParam, @Nullable Integer pathSegmentIndex) {
44+
@Nullable String header, @Nullable String queryParam, @Nullable String mediaTypeParam,
45+
@Nullable Integer pathSegmentIndex) {
4346

4447
this.header = header;
4548
this.queryParam = queryParam;
49+
this.mediaTypeParam = mediaTypeParam;
4650
this.pathSegmentIndex = pathSegmentIndex;
4751
}
4852

4953
/**
5054
* Configure the inserter to set a header.
5155
* @param header the name of the header to hold the version
5256
*/
57+
@Override
5358
public ApiVersionInserter.Builder useHeader(@Nullable String header) {
5459
this.header = header;
5560
return this;
5661
}
5762

58-
/**
59-
* Configure the inserter to set a query parameter.
60-
* @param queryParam the name of the query parameter to hold the version
61-
*/
63+
@Override
6264
public ApiVersionInserter.Builder useQueryParam(@Nullable String queryParam) {
6365
this.queryParam = queryParam;
6466
return this;
6567
}
6668

67-
/**
68-
* Configure the inserter to insert a path segment.
69-
* @param pathSegmentIndex the index of the path segment to hold the version
70-
*/
69+
@Override
70+
public ApiVersionInserter.Builder useMediaTypeParam(@Nullable String param) {
71+
this.mediaTypeParam = param;
72+
return this;
73+
}
74+
75+
@Override
7176
public ApiVersionInserter.Builder usePathSegment(@Nullable Integer pathSegmentIndex) {
7277
this.pathSegmentIndex = pathSegmentIndex;
7378
return this;
7479
}
7580

76-
/**
77-
* Format the version Object into a String using the given {@link ApiVersionFormatter}.
78-
* <p>By default, the version is formatted with {@link Object#toString()}.
79-
* @param versionFormatter the formatter to use
80-
*/
81+
@Override
8182
public ApiVersionInserter.Builder withVersionFormatter(ApiVersionFormatter versionFormatter) {
8283
this.versionFormatter = versionFormatter;
8384
return this;
8485
}
8586

86-
/**
87-
* Build the inserter.
88-
*/
8987
public ApiVersionInserter build() {
9088
return new DefaultApiVersionInserter(
91-
this.header, this.queryParam, this.pathSegmentIndex, this.versionFormatter);
89+
this.header, this.queryParam, this.mediaTypeParam, this.pathSegmentIndex,
90+
this.versionFormatter);
9291
}
9392

9493
}

spring-web/src/test/java/org/springframework/web/client/RestClientVersionTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.junit.jupiter.api.BeforeEach;
2727
import org.junit.jupiter.api.Test;
2828

29+
import org.springframework.http.MediaType;
2930
import org.springframework.http.client.JdkClientHttpRequestFactory;
3031

3132
import static org.assertj.core.api.Assertions.assertThat;
@@ -73,6 +74,12 @@ void queryParam() {
7374
expectRequest(request -> assertThat(request.getTarget()).isEqualTo("/path?api-version=1.2"));
7475
}
7576

77+
@Test
78+
void mediaTypeParam() {
79+
performRequest(ApiVersionInserter.useMediaTypeParam("v"));
80+
expectRequest(request -> assertThat(request.getHeaders().get("Content-Type")).isEqualTo("application/json;v=1.2"));
81+
}
82+
7683
@Test
7784
void pathSegmentIndexLessThanSize() {
7885
performRequest(ApiVersionInserter.builder().usePathSegment(0).withVersionFormatter(v -> "v" + v).build());
@@ -103,7 +110,8 @@ void defaultVersion() {
103110

104111
private void performRequest(ApiVersionInserter versionInserter) {
105112
restClientBuilder.apiVersionInserter(versionInserter).build()
106-
.get().uri("/path")
113+
.post().uri("/path")
114+
.contentType(MediaType.APPLICATION_JSON)
107115
.apiVersion(1.2)
108116
.retrieve()
109117
.body(String.class);

0 commit comments

Comments
 (0)