diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3098fba..2139b2b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### Version 13.17 + +* `Request` now accepts a `null` `Request.Body` and treats it as "no body", instead of throwing a + `NullPointerException` from `body()`, `charset()`, `isBinary()` and `length()`. A request with no + body reports a `null` body and charset, is binary, and has length 0. Its `toString()` prints the + request line and headers without a body (#1210). + ### Version 13.16 * Path-style expansion repeats the entry name for each collection or array value inside a map, diff --git a/core/src/main/java/feign/Request.java b/core/src/main/java/feign/Request.java index 822518066..e3ccb5e03 100644 --- a/core/src/main/java/feign/Request.java +++ b/core/src/main/java/feign/Request.java @@ -147,7 +147,7 @@ public static Request create( * @param httpMethod for the request. * @param url for the request. * @param headers to include. - * @param body of the request, can be {@literal null} + * @param body of the request, can be {@literal null}, meaning the request has no body. * @return a Request */ public static Request create( @@ -184,7 +184,7 @@ public static Request create( this.httpMethod = checkNotNull(method, "httpMethod of %s", method.name()); this.url = checkNotNull(url, "url"); this.headers = checkNotNull(headers, "headers of %s %s", method, url); - this.body = body; + this.body = body != null ? body : Body.empty(); this.requestTemplate = requestTemplate; protocolVersion = ProtocolVersion.HTTP_1_1; } @@ -308,7 +308,7 @@ public String toString() { builder.append(field).append(": ").append(value).append('\n'); } } - if (body != null) { + if (body() != null) { builder.append('\n').append(body.asString()); } return builder.toString(); diff --git a/core/src/test/java/feign/RequestTest.java b/core/src/test/java/feign/RequestTest.java index 9a40c1f52..5f0e87bca 100644 --- a/core/src/test/java/feign/RequestTest.java +++ b/core/src/test/java/feign/RequestTest.java @@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.assertThat; import feign.Request.Body; +import feign.Request.HttpMethod; +import java.util.Collections; import org.junit.jupiter.api.Test; public class RequestTest { @@ -34,4 +36,52 @@ void stringBodyIsEncodedWithUtf8NotThePlatformDefault() { assertThat(body.isBinary()).isFalse(); assertThat(body.asString()).isEqualTo(content); } + + @Test + void requestWithoutBodyHasNullBodyAndCharsetZeroLengthAndIsBinary() { + Request request = createGetRequestWithNullBody(); + + assertThat(request.body()).isNull(); + assertThat(request.charset()).isNull(); + assertThat(request.isBinary()).isTrue(); + assertThat(request.length()).isZero(); + } + + @Test + void toStringOfRequestWithoutBodyOmitsBodySection() { + Request request = createGetRequestWithNullBody(); + + assertThat(request).hasToString("GET http://example.com HTTP/1.1\n"); + } + + @Test + void requestBuiltFromTemplateWithoutBodyOmitsBodyFromToString() { + Request request = + new RequestTemplate() + .method(HttpMethod.GET) + .target("http://example.com") + .resolve(Collections.emptyMap()) + .request(); + + assertThat(request).hasToString("GET http://example.com HTTP/1.1\n"); + } + + @Test + void binaryBodyWithoutCharsetPrintsBinaryDataInToString() { + Request request = + new RequestTemplate() + .method(HttpMethod.POST) + .target("http://example.com") + .body(new byte[] {1, 2, 3}, null) + .resolve(Collections.emptyMap()) + .request(); + + assertThat(request) + .hasToString("POST http://example.com HTTP/1.1\nContent-Length: 3\n\nBinary data"); + } + + private static Request createGetRequestWithNullBody() { + return Request.create( + HttpMethod.GET, "http://example.com", Collections.emptyMap(), (Body) null, null); + } }