提交 07548db7 编写于 作者: Y Yuya Tanaka

Add @PartMap annotation.

上级 196c7d37
......@@ -280,6 +280,23 @@ final class RequestBuilder implements RequestInterceptor.RequestFacade {
}
}
break;
case PART_MAP:
if (value != null) { // Skip null values.
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
String entryName = entry.getKey().toString();
Object entryValue = entry.getValue();
if (entryValue != null) { // Skip null values.
if (entryValue instanceof TypedOutput) {
multipartBody.addPart(entryName, (TypedOutput) entryValue);
} else if (entryValue instanceof String) {
multipartBody.addPart(entryName, new TypedString((String) entryValue));
} else {
multipartBody.addPart(entryName, converter.toBody(entryValue));
}
}
}
}
break;
case BODY:
if (value == null) {
throw new IllegalArgumentException("Body parameter value must not be null.");
......
......@@ -38,6 +38,7 @@ import retrofit.http.Header;
import retrofit.http.Headers;
import retrofit.http.Multipart;
import retrofit.http.Part;
import retrofit.http.PartMap;
import retrofit.http.Path;
import retrofit.http.Query;
import retrofit.http.QueryMap;
......@@ -68,6 +69,7 @@ final class RestMethodInfo {
FIELD,
FIELD_MAP,
PART,
PART_MAP,
BODY,
HEADER
}
......@@ -411,6 +413,17 @@ final class RestMethodInfo {
gotPart = true;
paramNames[i] = name;
paramUsage[i] = ParamUsage.PART;
} else if (annotationType == PartMap.class) {
if (requestType != RequestType.MULTIPART) {
throw parameterError(i,
"@PartMap parameters can only be used with multipart encoding.");
}
if (!Map.class.isAssignableFrom(parameterType)) {
throw parameterError(i, "@PartMap parameter type must be Map.");
}
gotPart = true;
paramUsage[i] = ParamUsage.PART_MAP;
} else if (annotationType == Body.class) {
if (requestType != RequestType.SIMPLE) {
throw parameterError(i,
......
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package retrofit.http;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Denotes name and value parts of a multi-part request
* <p>
* Values of the map on which this annotation exists will be processed in one of three ways:
* <ul>
* <li>If the type implements {@link retrofit.mime.TypedOutput TypedOutput} the headers and
* body will be used directly.</li>
* <li>If the type is {@link String} the value will also be used directly with a {@code text/plain}
* content type.</li>
* <li>Other object types will be converted to an appropriate representation by calling {@link
* retrofit.converter.Converter#toBody(Object)}.</li>
* </ul>
* <p>
* <pre>
* &#64;Multipart
* &#64;POST("/upload")
* void upload(&#64;Part("file") TypedFile file, &#64;PartMap Map&lt;String, String&gt; params);
* </pre>
* <p>
*
* @see Multipart
* @see Part
*/
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface PartMap {
}
......@@ -33,6 +33,7 @@ import static retrofit.RestMethodInfo.ParamUsage.FIELD;
import static retrofit.RestMethodInfo.ParamUsage.FIELD_MAP;
import static retrofit.RestMethodInfo.ParamUsage.HEADER;
import static retrofit.RestMethodInfo.ParamUsage.PART;
import static retrofit.RestMethodInfo.ParamUsage.PART_MAP;
import static retrofit.RestMethodInfo.ParamUsage.PATH;
import static retrofit.RestMethodInfo.ParamUsage.QUERY;
import static retrofit.RestMethodInfo.ParamUsage.QUERY_MAP;
......@@ -490,6 +491,38 @@ public class RequestBuilderTest {
assertThat(two).contains("name=\"kit\"").endsWith("\r\nkat");
}
@Test public void multipartPartMap() throws Exception {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put("ping", "pong");
params.put("kit", new TypedString("kat"));
Request request = new Helper() //
.setMethod("POST") //
.setHasBody() //
.setUrl("http://example.com") //
.setPath("/foo/bar/") //
.setMultipart() //
.addPartMap("params", params) //
.build();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getHeaders()).hasSize(2);
assertThat(request.getHeaders().get(0).getName()).isEqualTo("Content-Type");
assertThat(request.getHeaders().get(1)).isEqualTo(new Header("Content-Length", "414"));
assertThat(request.getUrl()).isEqualTo("http://example.com/foo/bar/");
MultipartTypedOutput body = (MultipartTypedOutput) request.getBody();
List<byte[]> bodyParts = MimeHelper.getParts(body);
assertThat(bodyParts).hasSize(2);
Iterator<byte[]> iterator = bodyParts.iterator();
String one = new String(iterator.next(), "UTF-8");
assertThat(one).contains("name=\"ping\"\r\n").endsWith("\r\npong");
String two = new String(iterator.next(), "UTF-8");
assertThat(two).contains("name=\"kit\"").endsWith("\r\nkat");
}
@Test public void multipartNullRemovesPart() throws Exception {
Request request = new Helper() //
.setMethod("POST") //
......@@ -858,6 +891,13 @@ public class RequestBuilderTest {
return this;
}
Helper addPartMap(String name, Map<String, Object> values) {
paramNames.add(name);
paramUsages.add(PART_MAP);
args.add(values);
return this;
}
Helper setBody(Object value) {
paramNames.add(null);
paramUsages.add(BODY);
......
......@@ -28,6 +28,7 @@ import retrofit.http.PATCH;
import retrofit.http.POST;
import retrofit.http.PUT;
import retrofit.http.Part;
import retrofit.http.PartMap;
import retrofit.http.Path;
import retrofit.http.Query;
import retrofit.http.QueryMap;
......@@ -908,6 +909,21 @@ public class RestMethodInfoTest {
assertThat(methodInfo.requestType).isEqualTo(MULTIPART);
}
@Test public void partMapMultipart() {
class Example {
@Multipart @PUT("/")
Response a(@Part("a") TypedOutput a, @PartMap Map<String, String> b) {
return null;
}
}
Method method = TestingUtils.getMethod(Example.class, "a");
RestMethodInfo methodInfo = new RestMethodInfo(method);
methodInfo.init();
assertThat(methodInfo.requestType).isEqualTo(MULTIPART);
}
@Test public void implicitMultipartForbidden() {
class Example {
@POST("/") Response a(@Part("a") int a) {
......@@ -926,6 +942,24 @@ public class RestMethodInfoTest {
}
}
@Test public void implicitMultipartWithPartMapForbidden() {
class Example {
@POST("/") Response a(@PartMap Map<String, String> params) {
return null;
}
}
Method method = TestingUtils.getMethod(Example.class, "a");
RestMethodInfo methodInfo = new RestMethodInfo(method);
try {
methodInfo.init();
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage(
"Example.a: @PartMap parameters can only be used with multipart encoding. (parameter #1)");
}
}
@Test public void multipartFailsOnNonBodyMethod() {
class Example {
@Multipart @GET("/") Response a() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册