From 419276aa5413e548bdb21e5958d3e0bfe7396c6b Mon Sep 17 00:00:00 2001 From: Thiago Yudi Fukunaga Date: Sun, 15 Nov 2020 19:22:31 -0400 Subject: [PATCH 1/3] add type to multipart annotation to include the specified type to RequestBody contentType --- .../main/java/retrofit2/RequestBuilder.java | 5 +- .../main/java/retrofit2/RequestFactory.java | 7 ++- .../main/java/retrofit2/http/Multipart.java | 10 +++- .../java/retrofit2/RequestFactoryTest.java | 48 +++++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/retrofit/src/main/java/retrofit2/RequestBuilder.java b/retrofit/src/main/java/retrofit2/RequestBuilder.java index d47f61fea5..bc3bb2080d 100644 --- a/retrofit/src/main/java/retrofit2/RequestBuilder.java +++ b/retrofit/src/main/java/retrofit2/RequestBuilder.java @@ -72,7 +72,8 @@ final class RequestBuilder { @Nullable MediaType contentType, boolean hasBody, boolean isFormEncoded, - boolean isMultipart) { + boolean isMultipart, + String multipartType) { this.method = method; this.baseUrl = baseUrl; this.relativeUrl = relativeUrl; @@ -92,7 +93,7 @@ final class RequestBuilder { } else if (isMultipart) { // Will be set to 'body' in 'build'. multipartBuilder = new MultipartBody.Builder(); - multipartBuilder.setType(MultipartBody.FORM); + multipartBuilder.setType(MediaType.get(multipartType)); } } diff --git a/retrofit/src/main/java/retrofit2/RequestFactory.java b/retrofit/src/main/java/retrofit2/RequestFactory.java index bea554efae..34f0de33d7 100644 --- a/retrofit/src/main/java/retrofit2/RequestFactory.java +++ b/retrofit/src/main/java/retrofit2/RequestFactory.java @@ -76,6 +76,7 @@ static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { private final boolean hasBody; private final boolean isFormEncoded; private final boolean isMultipart; + private final String multipartType; private final ParameterHandler[] parameterHandlers; final boolean isKotlinSuspendFunction; @@ -89,6 +90,7 @@ static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { hasBody = builder.hasBody; isFormEncoded = builder.isFormEncoded; isMultipart = builder.isMultipart; + multipartType = builder.multipartType; parameterHandlers = builder.parameterHandlers; isKotlinSuspendFunction = builder.isKotlinSuspendFunction; } @@ -116,7 +118,8 @@ okhttp3.Request create(Object[] args) throws IOException { contentType, hasBody, isFormEncoded, - isMultipart); + isMultipart, + multipartType); if (isKotlinSuspendFunction) { // The Continuation is the last parameter and the handlers array contains null at that index. @@ -161,6 +164,7 @@ static final class Builder { boolean hasBody; boolean isFormEncoded; boolean isMultipart; + String multipartType; @Nullable String relativeUrl; @Nullable Headers headers; @Nullable MediaType contentType; @@ -251,6 +255,7 @@ private void parseMethodAnnotation(Annotation annotation) { throw methodError(method, "Only one encoding annotation is allowed."); } isMultipart = true; + multipartType = ((Multipart) annotation).type(); } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError(method, "Only one encoding annotation is allowed."); diff --git a/retrofit/src/main/java/retrofit2/http/Multipart.java b/retrofit/src/main/java/retrofit2/http/Multipart.java index 9ae1b5607d..c2b05ab0eb 100644 --- a/retrofit/src/main/java/retrofit2/http/Multipart.java +++ b/retrofit/src/main/java/retrofit2/http/Multipart.java @@ -22,6 +22,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import okhttp3.HttpUrl; + /** * Denotes that the request body is multi-part. Parts should be declared as parameters and annotated * with {@link Part @Part}. @@ -29,4 +31,10 @@ @Documented @Target(METHOD) @Retention(RUNTIME) -public @interface Multipart {} +public @interface Multipart { + /** + * Sets the type(MediaType) on MultipartBody. When calling the + */ + + String type() default "multipart/form-data"; +} diff --git a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java index e8c21752ab..4b9dd13bbf 100644 --- a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java +++ b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java @@ -3252,6 +3252,54 @@ Call method(@Tag List one, @Tag List two) { } } + @Test + public void shouldBuildRequestBodyWithContentTypeSuccessfully() throws IOException { + class Example { + @Multipart(type = "multipart/form-data; charset=utf-8") // + @POST("/foo/bar/") // + Call method(@Part("ping") String ping, @Part("kit") RequestBody kit) { + return null; + } + } + + Request request = buildRequest(Example.class, "pong", RequestBody.create(TEXT_PLAIN, "kat")); + assertThat(request.method()).isEqualTo("POST"); + assertThat(request.headers().size()).isZero(); + assertThat(request.url().toString()).isEqualTo("http://example.com/foo/bar/"); + + RequestBody body = request.body(); + assertThat(body.contentType().toString()).startsWith("multipart/form-data; charset=utf-8; boundary="); + + Buffer buffer = new Buffer(); + body.writeTo(buffer); + String bodyString = buffer.readUtf8(); + + assertThat(bodyString) + .contains("Content-Disposition: form-data;") + .contains("name=\"ping\"\r\n") + .contains("\r\npong\r\n--") + .contains("name=\"kit\"") + .contains("\r\nkat\r\n--"); + } + + @Test + public void shouldFailRequestBodyWithContentTypeInvalid() throws IOException { + class Example { + @Multipart(type = "invalid-type") // + @POST("/foo/bar/") // + Call method(@Part("ping") String ping, @Part("kit") RequestBody kit) { + return null; + } + } + + try { + buildRequest(Example.class, "pong", RequestBody.create(TEXT_PLAIN, "kat")); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessageContaining("No subtype found for: \"invalid-type\""); + } + } + private static void assertBody(RequestBody body, String expected) { assertThat(body).isNotNull(); Buffer buffer = new Buffer(); From 649fd25e10a3a3007a3e5aa1bcf8f2d679a26d1c Mon Sep 17 00:00:00 2001 From: Thiago Yudi Fukunaga Date: Sun, 15 Nov 2020 19:59:28 -0400 Subject: [PATCH 2/3] fix formatting to pass the lints --- retrofit/src/main/java/retrofit2/http/Multipart.java | 9 ++++----- retrofit/src/test/java/retrofit2/RequestFactoryTest.java | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/retrofit/src/main/java/retrofit2/http/Multipart.java b/retrofit/src/main/java/retrofit2/http/Multipart.java index c2b05ab0eb..f466fa912b 100644 --- a/retrofit/src/main/java/retrofit2/http/Multipart.java +++ b/retrofit/src/main/java/retrofit2/http/Multipart.java @@ -32,9 +32,8 @@ @Target(METHOD) @Retention(RUNTIME) public @interface Multipart { - /** - * Sets the type(MediaType) on MultipartBody. When calling the - */ - - String type() default "multipart/form-data"; + /** + * Sets the type(MediaType) on MultipartBody. When calling the + */ + String type() default "multipart/form-data"; } diff --git a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java index 4b9dd13bbf..7bf2ad6b35 100644 --- a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java +++ b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java @@ -3291,7 +3291,6 @@ Call method(@Part("ping") String ping, @Part("kit") RequestBody ki return null; } } - try { buildRequest(Example.class, "pong", RequestBody.create(TEXT_PLAIN, "kat")); fail(); From cd36e8c1797abe871d520631b0eeed21aaf1fd81 Mon Sep 17 00:00:00 2001 From: Thiago Yudi Fukunaga Date: Sun, 15 Nov 2020 20:33:21 -0400 Subject: [PATCH 3/3] fix formatting to pass the lints --- .../src/main/java/retrofit2/RequestFactory.java | 2 +- .../src/main/java/retrofit2/http/Multipart.java | 6 +----- .../src/test/java/retrofit2/RequestFactoryTest.java | 13 +++++++------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/retrofit/src/main/java/retrofit2/RequestFactory.java b/retrofit/src/main/java/retrofit2/RequestFactory.java index 34f0de33d7..cc66552e74 100644 --- a/retrofit/src/main/java/retrofit2/RequestFactory.java +++ b/retrofit/src/main/java/retrofit2/RequestFactory.java @@ -164,7 +164,7 @@ static final class Builder { boolean hasBody; boolean isFormEncoded; boolean isMultipart; - String multipartType; + @Nullable String multipartType; @Nullable String relativeUrl; @Nullable Headers headers; @Nullable MediaType contentType; diff --git a/retrofit/src/main/java/retrofit2/http/Multipart.java b/retrofit/src/main/java/retrofit2/http/Multipart.java index f466fa912b..fe5ad5035d 100644 --- a/retrofit/src/main/java/retrofit2/http/Multipart.java +++ b/retrofit/src/main/java/retrofit2/http/Multipart.java @@ -22,8 +22,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import okhttp3.HttpUrl; - /** * Denotes that the request body is multi-part. Parts should be declared as parameters and annotated * with {@link Part @Part}. @@ -32,8 +30,6 @@ @Target(METHOD) @Retention(RUNTIME) public @interface Multipart { - /** - * Sets the type(MediaType) on MultipartBody. When calling the - */ + /** Sets the type(MediaType) on MultipartBody. When calling the */ String type() default "multipart/form-data"; } diff --git a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java index 7bf2ad6b35..450925e052 100644 --- a/retrofit/src/test/java/retrofit2/RequestFactoryTest.java +++ b/retrofit/src/test/java/retrofit2/RequestFactoryTest.java @@ -3268,18 +3268,19 @@ Call method(@Part("ping") String ping, @Part("kit") RequestBody ki assertThat(request.url().toString()).isEqualTo("http://example.com/foo/bar/"); RequestBody body = request.body(); - assertThat(body.contentType().toString()).startsWith("multipart/form-data; charset=utf-8; boundary="); + assertThat(body.contentType().toString()) + .startsWith("multipart/form-data; charset=utf-8; boundary="); Buffer buffer = new Buffer(); body.writeTo(buffer); String bodyString = buffer.readUtf8(); assertThat(bodyString) - .contains("Content-Disposition: form-data;") - .contains("name=\"ping\"\r\n") - .contains("\r\npong\r\n--") - .contains("name=\"kit\"") - .contains("\r\nkat\r\n--"); + .contains("Content-Disposition: form-data;") + .contains("name=\"ping\"\r\n") + .contains("\r\npong\r\n--") + .contains("name=\"kit\"") + .contains("\r\nkat\r\n--"); } @Test