Skip to content

Commit

Permalink
add type to multipart annotation to include the specified type to Req…
Browse files Browse the repository at this point in the history
…uestBody contentType
  • Loading branch information
thiagoyf committed Nov 15, 2020
1 parent 7128bf3 commit 419276a
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 4 deletions.
5 changes: 3 additions & 2 deletions retrofit/src/main/java/retrofit2/RequestBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
}
}

Expand Down
7 changes: 6 additions & 1 deletion retrofit/src/main/java/retrofit2/RequestFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
Expand Down
10 changes: 9 additions & 1 deletion retrofit/src/main/java/retrofit2/http/Multipart.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@
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}.
*/
@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";
}
48 changes: 48 additions & 0 deletions retrofit/src/test/java/retrofit2/RequestFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3252,6 +3252,54 @@ Call<ResponseBody> method(@Tag List<String> one, @Tag List<Long> two) {
}
}

@Test
public void shouldBuildRequestBodyWithContentTypeSuccessfully() throws IOException {
class Example {
@Multipart(type = "multipart/form-data; charset=utf-8") //
@POST("/foo/bar/") //
Call<ResponseBody> 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<ResponseBody> 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();
Expand Down

0 comments on commit 419276a

Please sign in to comment.