diff --git a/feign-form/src/main/java/feign/form/FormEncoder.java b/feign-form/src/main/java/feign/form/FormEncoder.java index 8d6ab02..047b3af 100644 --- a/feign-form/src/main/java/feign/form/FormEncoder.java +++ b/feign-form/src/main/java/feign/form/FormEncoder.java @@ -70,15 +70,23 @@ public FormEncoder () { * @param delegate delegate encoder, if this encoder couldn't encode object. */ public FormEncoder (Encoder delegate) { - this.delegate = delegate; + this(delegate, asList( + new MultipartFormContentProcessor(delegate), + new UrlencodedFormContentProcessor() + )); + } - val list = asList( - new MultipartFormContentProcessor(delegate), - new UrlencodedFormContentProcessor() - ); + /** + * Constructor with specified delegate encoder and custom processors. + * + * @param delegate delegate encoder, if this encoder couldn't encode object. + * @param customProcessors processors for mutlipart and url-encoded content types. + */ + public FormEncoder (Encoder delegate, Iterable customProcessors) { + this.delegate = delegate; - processors = new HashMap(list.size(), 1.F); - for (ContentProcessor processor : list) { + processors = new HashMap(2, 1.F); + for (ContentProcessor processor : customProcessors) { processors.put(processor.getSupportedContentType(), processor); } } diff --git a/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java b/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java index 7a655d9..994ff9d 100644 --- a/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java +++ b/feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java @@ -27,6 +27,7 @@ import java.util.LinkedList; import java.util.Map; +import feign.form.multipart.ManyFormDataWriter; import lombok.experimental.FieldDefaults; import lombok.val; @@ -65,6 +66,7 @@ public MultipartFormContentProcessor (Encoder delegate) { writers = new LinkedList<>(); addWriter(new ByteArrayWriter()); addWriter(new FormDataWriter()); + addWriter(new ManyFormDataWriter()); addWriter(new SingleFileWriter()); addWriter(new ManyFilesWriter()); addWriter(new SingleParameterWriter()); diff --git a/feign-form/src/main/java/feign/form/multipart/ManyFormDataWriter.java b/feign-form/src/main/java/feign/form/multipart/ManyFormDataWriter.java new file mode 100644 index 0000000..6557d63 --- /dev/null +++ b/feign-form/src/main/java/feign/form/multipart/ManyFormDataWriter.java @@ -0,0 +1,37 @@ +package feign.form.multipart; + +import feign.codec.EncodeException; +import feign.form.FormData; +import lombok.val; + +public class ManyFormDataWriter implements Writer { + private final FormDataWriter formDataWriter = new FormDataWriter(); + @Override + public void write(Output output, String boundary, String key, Object value) throws EncodeException { + if (value instanceof FormData[]) { + for (FormData formData : (FormData[]) value) { + formDataWriter.write(output, boundary, key, formData); + } + } else if (value instanceof Iterable) { + val fields = (Iterable)value; + for (Object formData : fields) { + formDataWriter.write(output, boundary, key, formData); + } + } else { + throw new IllegalStateException(); + } + } + + @Override + public boolean isApplicable(Object value) { + if (value instanceof FormData[]) { + return true; + } + if (value instanceof Iterable) { + val iterable = (Iterable) value; + val iterator = iterable.iterator(); + return iterator.hasNext() && iterator.next() instanceof FormData; + } + return false; + } +} diff --git a/feign-form/src/test/java/feign/form/BasicClientTest.java b/feign-form/src/test/java/feign/form/BasicClientTest.java index dc056af..f73418c 100644 --- a/feign-form/src/test/java/feign/form/BasicClientTest.java +++ b/feign-form/src/test/java/feign/form/BasicClientTest.java @@ -169,6 +169,17 @@ void testFormData () throws Exception { .isEqualTo("popa.txt:application/custom-type"); } + @Test + void testMultipleFormDataList () throws Exception { + val formData = asList( + new FormData("application/custom-type", "file1.txt", "Allo".getBytes("UTF-8")), + new FormData("image/jpeg", "file2.jpg", "Allo".getBytes("UTF-8")) + ); + + assertThat(API.uploadFormData(formData)) + .isEqualTo("file1.txt:application/custom-type,file2.jpg:image/jpeg"); + } + @Test void testSubmitRepeatableQueryParam () throws Exception { val names = new String[] { "Milada", "Thais" }; diff --git a/feign-form/src/test/java/feign/form/Server.java b/feign-form/src/test/java/feign/form/Server.java index 4d81f10..ab6194e 100644 --- a/feign-form/src/test/java/feign/form/Server.java +++ b/feign-form/src/test/java/feign/form/Server.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import lombok.val; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -197,6 +198,19 @@ public ResponseEntity uploadFormData ( return ResponseEntity.status(status).body(file.getOriginalFilename() + ':' + file.getContentType()); } + @PostMapping(path = "/upload/many_form_data", consumes = MULTIPART_FORM_DATA_VALUE) + public ResponseEntity uploadFormData ( + @RequestPart("file[]") List files + ) { + val status = files != null + ? OK + : I_AM_A_TEAPOT; + val response = files.stream() + .map(file -> file.getOriginalFilename() + ':' + file.getContentType()) + .collect(Collectors.joining(",")); + return ResponseEntity.status(status).body(response); + } + @PostMapping(path = "/submit/url", consumes = APPLICATION_FORM_URLENCODED_VALUE) public ResponseEntity submitRepeatableQueryParam ( @RequestParam("names") String[] names diff --git a/feign-form/src/test/java/feign/form/TestClient.java b/feign-form/src/test/java/feign/form/TestClient.java index 92b5251..106ce64 100644 --- a/feign-form/src/test/java/feign/form/TestClient.java +++ b/feign-form/src/test/java/feign/form/TestClient.java @@ -72,6 +72,10 @@ public interface TestClient { @Headers("Content-Type: multipart/form-data") String uploadFormData (@Param("file") FormData formData); + @RequestLine("POST /upload/many_form_data") + @Headers("Content-Type: multipart/form-data") + String uploadFormData (@Param("file[]") List formData); + @RequestLine("POST /submit/url") @Headers("Content-Type: application/x-www-form-urlencoded") String submitRepeatableQueryParam (@Param("names") String[] names);