Skip to content

Commit 80f0475

Browse files
Adrian ColeAdrian Cole
authored andcommitted
Allows customized request construction by exposing Request.create()
Users may wish to override percent encoding of query parameters. closes OpenFeign#227
1 parent 362f5e6 commit 80f0475

4 files changed

Lines changed: 98 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
### Version 8.2
2+
* Allows customized request construction by exposing `Request.create()`
23
* Adds JMH benchmark module
34
* Enforces source compatibility with animal-sniffer
45

core/src/main/java/feign/Request.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import java.nio.charset.Charset;
1919
import java.util.Collection;
20-
import java.util.Collections;
21-
import java.util.LinkedHashMap;
2220
import java.util.Map;
2321

2422
import static feign.Util.checkNotNull;
@@ -29,6 +27,15 @@
2927
*/
3028
public final class Request {
3129

30+
/**
31+
* No parameters can be null except {@code body} and {@code charset}. All parameters must be
32+
* effectively immutable, via safe copies, not mutating or otherwise.
33+
*/
34+
public static Request create(String method, String url, Map<String, Collection<String>> headers,
35+
byte[] body, Charset charset) {
36+
return new Request(method, url, headers, body, charset);
37+
}
38+
3239
private final String method;
3340
private final String url;
3441
private final Map<String, Collection<String>> headers;
@@ -39,11 +46,7 @@ public final class Request {
3946
Charset charset) {
4047
this.method = checkNotNull(method, "method of %s", url);
4148
this.url = checkNotNull(url, "url");
42-
LinkedHashMap<String, Collection<String>>
43-
copyOf =
44-
new LinkedHashMap<String, Collection<String>>();
45-
copyOf.putAll(checkNotNull(headers, "headers of %s %s", method, url));
46-
this.headers = Collections.unmodifiableMap(copyOf);
49+
this.headers = checkNotNull(headers, "headers of %s %s", method, url);
4750
this.body = body; // nullable
4851
this.charset = charset; // nullable
4952
}

core/src/main/java/feign/RequestTemplate.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@
4646
public final class RequestTemplate implements Serializable {
4747

4848
private static final long serialVersionUID = 1L;
49-
private final Map<String, Collection<String>>
50-
queries =
49+
private final Map<String, Collection<String>> queries =
5150
new LinkedHashMap<String, Collection<String>>();
52-
private final Map<String, Collection<String>>
53-
headers =
51+
private final Map<String, Collection<String>> headers =
5452
new LinkedHashMap<String, Collection<String>>();
5553
private String method;
5654
/* final to encourage mutable use vs replacing the object. */
@@ -221,8 +219,14 @@ public RequestTemplate resolve(Map<String, ?> unencoded) {
221219

222220
/* roughly analogous to {@code javax.ws.rs.client.Target.request()}. */
223221
public Request request() {
224-
return new Request(method, new StringBuilder(url).append(queryLine()).toString(),
225-
headers, body, charset);
222+
Map<String, Collection<String>> safeCopy = new LinkedHashMap<String, Collection<String>>();
223+
safeCopy.putAll(headers);
224+
return Request.create(
225+
method,
226+
new StringBuilder(url).append(queryLine()).toString(),
227+
Collections.unmodifiableMap(safeCopy),
228+
body, charset
229+
);
226230
}
227231

228232
/* @see Request#method() */
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2015 Netflix, Inc.
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+
package feign;
17+
18+
import com.squareup.okhttp.mockwebserver.MockResponse;
19+
import com.squareup.okhttp.mockwebserver.rule.MockWebServerRule;
20+
21+
import org.junit.Rule;
22+
import org.junit.Test;
23+
24+
import feign.Target.HardCodedTarget;
25+
26+
import static feign.assertj.MockWebServerAssertions.assertThat;
27+
28+
public class TargetTest {
29+
30+
@Rule
31+
public final MockWebServerRule server = new MockWebServerRule();
32+
33+
interface TestQuery {
34+
35+
@RequestLine("GET /{path}?query={query}")
36+
Response get(@Param("path") String path, @Param("query") String query);
37+
}
38+
39+
@Test
40+
public void baseCaseQueryParamsArePercentEncoded() throws InterruptedException {
41+
server.enqueue(new MockResponse());
42+
43+
String baseUrl = server.getUrl("/default").toString();
44+
45+
Feign.builder().target(TestQuery.class, baseUrl).get("slash/foo", "slash/bar");
46+
47+
assertThat(server.takeRequest()).hasPath("/default/slash/foo?query=slash%2Fbar");
48+
}
49+
50+
/**
51+
* Per <a href="https://github.com/Netflix/feign/issues/227">#227</a>, some may want to opt out of
52+
* percent encoding. Here's how.
53+
*/
54+
@Test
55+
public void targetCanCreateCustomRequest() throws InterruptedException {
56+
server.enqueue(new MockResponse());
57+
58+
String baseUrl = server.getUrl("/default").toString();
59+
Target<TestQuery> custom = new HardCodedTarget<TestQuery>(TestQuery.class, baseUrl) {
60+
61+
@Override
62+
public Request apply(RequestTemplate input) {
63+
Request urlEncoded = super.apply(input);
64+
return Request.create(
65+
urlEncoded.method(),
66+
urlEncoded.url().replace("%2F", "/"),
67+
urlEncoded.headers(),
68+
urlEncoded.body(), urlEncoded.charset()
69+
);
70+
}
71+
};
72+
73+
Feign.builder().target(custom).get("slash/foo", "slash/bar");
74+
75+
assertThat(server.takeRequest()).hasPath("/default/slash/foo?query=slash/bar");
76+
}
77+
}

0 commit comments

Comments
 (0)