Skip to content

Commit 9c545e3

Browse files
GuiSimxxlabaza
authored andcommitted
Added FormData object (OpenFeign#38)
* Added FormData object * Fixed PMD checks * Updated README with FormData example * Added FormData creation example
1 parent fcfe0a5 commit 9c545e3

File tree

8 files changed

+136
-5
lines changed

8 files changed

+136
-5
lines changed

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,37 @@ interface SomeApi {
8080

8181
...
8282

83+
// File parameter
8384
@RequestLine("POST /send_photo")
8485
@Headers("Content-Type: multipart/form-data")
8586
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") File photo);
8687

88+
// byte[] parameter
89+
@RequestLine("POST /send_photo")
90+
@Headers("Content-Type: multipart/form-data")
91+
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") byte[] photo);
92+
93+
// FormData parameter
94+
@RequestLine("POST /send_photo")
95+
@Headers("Content-Type: multipart/form-data")
96+
void sendPhoto (@Param("is_public") Boolean isPublic, @Param("photo") FormData photo);
8797
...
8898

8999
}
90100
```
91101

92-
In example above, we send file in parameter named **photo** with additional field in form **is_public**.
102+
In the example above, the `sendPhoto` method uses the `photo` parameter using three different supported types.
93103

94-
> **IMPORTANT:** You can specify your files in API method by declaring type **File** or **byte[]**.
104+
* `File` will use the File's extension to detect the `Content-Type`.
105+
* `byte[]` will use `application/octet-stream` as `Content-Type`.
106+
* `FormData` will use the `FormData`'s `Content-Type`.
107+
108+
`FormData` is custom object that wraps a `byte[]` and defines a `Content-Type` like this:
109+
110+
```java
111+
FormData formData = new FormData("image/png", myDataAsByteArray);
112+
someApi.sendPhoto(true, formData);
113+
```
95114

96115
### Spring MultipartFile and Spring Cloud Netflix @FeignClient support
97116

feign-form/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ limitations under the License.
3737
<maven.compiler.target>1.6</maven.compiler.target>
3838
</properties>
3939

40+
<dependencies>
41+
<dependency>
42+
<groupId>com.google.code.findbugs</groupId>
43+
<artifactId>annotations</artifactId>
44+
<version>3.0.1</version>
45+
</dependency>
46+
</dependencies>
47+
4048
<build>
4149
<plugins>
4250
<plugin>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2018 Artem Labazin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package feign.form;
18+
19+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20+
21+
/**
22+
* This object encapsulates a byte array and its associated content type.
23+
* Use if if you want to specify the content type of your provided byte array.
24+
*/
25+
26+
@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
27+
public final class FormData {
28+
private final String contentType;
29+
private final byte[] data;
30+
31+
public FormData (String contentType, byte[] data) {
32+
this.contentType = contentType;
33+
this.data = data;
34+
}
35+
36+
public String getContentType () {
37+
return contentType;
38+
}
39+
40+
public byte[] getData () {
41+
return data;
42+
}
43+
}

feign-form/src/main/java/feign/form/MultipartFormContentProcessor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import feign.codec.Encoder;
3030
import feign.form.multipart.ByteArrayWriter;
3131
import feign.form.multipart.DelegateWriter;
32+
import feign.form.multipart.FormDataWriter;
3233
import feign.form.multipart.ManyFilesWriter;
3334
import feign.form.multipart.Output;
3435
import feign.form.multipart.ParameterWriter;
@@ -57,6 +58,7 @@ public class MultipartFormContentProcessor implements ContentProcessor {
5758
public MultipartFormContentProcessor (Encoder delegate) {
5859
writers = new ArrayList<Writer>(6);
5960
addWriter(new ByteArrayWriter());
61+
addWriter(new FormDataWriter());
6062
addWriter(new SingleFileWriter());
6163
addWriter(new ManyFilesWriter());
6264
addWriter(new ParameterWriter());
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2018 Artem Labazin
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package feign.form.multipart;
18+
19+
import feign.form.FormData;
20+
21+
import lombok.val;
22+
23+
public class FormDataWriter extends AbstractWriter {
24+
@Override
25+
public boolean isApplicable (Object value) {
26+
return value instanceof FormData;
27+
}
28+
29+
@Override
30+
protected void write (Output output, String key, Object value) throws Exception {
31+
val formData = (FormData) value;
32+
writeFileMetadata(output, key, null, formData.getContentType());
33+
output.write(formData.getData());
34+
}
35+
}

feign-form/src/test/java/feign/form/BasicClientTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.nio.file.Paths;
2929
import java.util.Map;
3030
import lombok.val;
31+
import org.apache.tomcat.util.http.fileupload.IOUtils;
3132
import org.junit.Assert;
3233
import org.junit.Test;
3334
import org.junit.runner.RunWith;
@@ -158,4 +159,11 @@ public void testUnknownTypeFile() throws Exception {
158159
val stringResponse = api.uploadUnknownType(path.toFile());
159160
Assert.assertEquals("application/octet-stream", stringResponse);
160161
}
162+
163+
@Test
164+
public void testFormData() throws Exception {
165+
val formData = new FormData("application/custom-type", "Allo".getBytes("UTF-8"));
166+
val stringResponse = api.uploadFormData(formData);
167+
Assert.assertEquals("application/custom-type", stringResponse);
168+
}
161169
}

feign-form/src/test/java/feign/form/Server.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,15 @@ public ResponseEntity<String> uploadUnknownType (@RequestPart("file") MultipartF
178178
: I_AM_A_TEAPOT;
179179
return ResponseEntity.status(status).body(file.getContentType());
180180
}
181+
182+
@PostMapping(
183+
path = "/upload/form_data",
184+
consumes = MULTIPART_FORM_DATA_VALUE
185+
)
186+
public ResponseEntity<String> uploadFormData (@RequestPart("file") MultipartFile file) {
187+
val status = file != null
188+
? OK
189+
: I_AM_A_TEAPOT;
190+
return ResponseEntity.status(status).body(file.getContentType());
191+
}
181192
}

feign-form/src/test/java/feign/form/TestClient.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
package feign.form;
1818

19+
import java.io.File;
20+
import java.util.List;
21+
import java.util.Map;
22+
1923
import feign.Headers;
2024
import feign.Param;
2125
import feign.QueryMap;
2226
import feign.RequestLine;
2327
import feign.Response;
24-
import java.io.File;
25-
import java.util.List;
26-
import java.util.Map;
2728

2829
/**
2930
*
@@ -69,4 +70,8 @@ public interface TestClient {
6970
@RequestLine("POST /upload/unknown_type")
7071
@Headers("Content-Type: multipart/form-data")
7172
String uploadUnknownType (@Param("file") File file);
73+
74+
@RequestLine("POST /upload/form_data")
75+
@Headers("Content-Type: multipart/form-data")
76+
String uploadFormData (@Param("file") FormData formData);
7277
}

0 commit comments

Comments
 (0)