29
29
import io .micronaut .core .util .CollectionUtils ;
30
30
import io .micronaut .core .util .StringUtils ;
31
31
import io .micronaut .core .util .SupplierUtil ;
32
+ import io .micronaut .function .aws .proxy .multipart .MultipartDataDecoder ;
32
33
import io .micronaut .http .CaseInsensitiveMutableHttpHeaders ;
33
34
import io .micronaut .http .FullHttpRequest ;
34
35
import io .micronaut .http .HttpMethod ;
41
42
import io .micronaut .http .body .stream .AvailableByteArrayBody ;
42
43
import io .micronaut .http .cookie .Cookie ;
43
44
import io .micronaut .http .cookie .Cookies ;
45
+ import io .micronaut .http .multipart .CompletedFileUpload ;
44
46
import io .micronaut .http .uri .UriBuilder ;
45
47
import io .micronaut .servlet .http .ByteArrayByteBuffer ;
46
48
import io .micronaut .servlet .http .MutableServletHttpRequest ;
@@ -91,6 +93,8 @@ public abstract class ApiGatewayServletRequest<T, REQ, RES> implements MutableSe
91
93
private Supplier <Optional <T >> body ;
92
94
private T parsedBody ;
93
95
private T overriddenBody ;
96
+ private final Supplier <Optional <MultipartDataDecoder >> multipartDataDecoder ;
97
+ private Map <String , CompletedFileUpload > fileUploads ;
94
98
95
99
private ByteArrayByteBuffer <T > servletByteBuffer ;
96
100
@@ -111,6 +115,19 @@ protected ApiGatewayServletRequest(
111
115
T built = parsedBody != null ? parsedBody : (T ) bodyBuilder .buildBody (this ::getInputStream , this );
112
116
return Optional .ofNullable (built );
113
117
});
118
+ this .multipartDataDecoder = SupplierUtil .memoized (() -> {
119
+ try {
120
+ MediaType contentType = getContentType ().orElse (null );
121
+ if (MediaType .MULTIPART_FORM_DATA_TYPE .equals (contentType )) {
122
+ return Optional .of (new MultipartDataDecoder (getBodyBytes (), getHeaders (), getCharacterEncoding ()));
123
+ }
124
+ } catch (IOException e ) {
125
+ if (log .isDebugEnabled ()) {
126
+ log .debug ("Error decoding multipart form data: {}" , e .getMessage (), e );
127
+ }
128
+ }
129
+ return Optional .empty ();
130
+ });
114
131
}
115
132
116
133
@ Override
@@ -266,16 +283,38 @@ public <B> MutableHttpRequest<B> body(B body) {
266
283
*/
267
284
protected MapListOfStringAndMapStringMutableHttpParameters getParametersFromBody (Map <String , String > queryStringParameters ) {
268
285
Map <String , List <String >> parameters = null ;
269
- try {
270
- parameters = new QueryStringDecoder (new String (getBodyBytes (), getCharacterEncoding ()), false ).parameters ();
271
- } catch (IOException ex ) {
272
- if (log .isDebugEnabled ()) {
273
- log .debug ("Error decoding form data: " + ex .getMessage (), ex );
286
+ MediaType contentType = getContentType ().orElse (MediaType .APPLICATION_JSON_TYPE );
287
+
288
+ if (MediaType .APPLICATION_FORM_URLENCODED_TYPE .equals (contentType )) {
289
+ try {
290
+ parameters = new QueryStringDecoder (new String (getBodyBytes (), getCharacterEncoding ()), false ).parameters ();
291
+ } catch (IOException ex ) {
292
+ if (log .isDebugEnabled ()) {
293
+ log .debug ("Error decoding form data: {}" , ex .getMessage (), ex );
294
+ }
274
295
}
296
+ } else if (MediaType .MULTIPART_FORM_DATA_TYPE .equals (contentType )) {
297
+ parameters = multipartDataDecoder .get ()
298
+ .map (MultipartDataDecoder ::parameters )
299
+ .orElse (Collections .emptyMap ());
275
300
}
301
+
276
302
return new MapListOfStringAndMapStringMutableHttpParameters (conversionService , parameters , queryStringParameters );
277
303
}
278
304
305
+ /**
306
+ * Gets a map of uploaded files from the multipart request.
307
+ * @return A map of field names to file uploads
308
+ */
309
+ public Map <String , CompletedFileUpload > getFileUploads () {
310
+ if (fileUploads != null ) {
311
+ log .trace ("Skipping decoding file uploads as they have already been processed" );
312
+ } else {
313
+ fileUploads = multipartDataDecoder .get ().map (MultipartDataDecoder ::fileUploads ).orElse (Collections .emptyMap ());
314
+ }
315
+ return Collections .unmodifiableMap (fileUploads );
316
+ }
317
+
279
318
@ Override
280
319
public void setConversionService (ConversionService conversionService ) {
281
320
this .conversionService = conversionService ;
@@ -355,6 +394,7 @@ protected MutableHttpParameters getParameters(@NonNull Supplier<Map<String, Stri
355
394
Map <String , List <String >> multi = multiQueryStringParametersSupplier .get ();
356
395
Map <String , String > single = queryStringParametersSupplier .get ();
357
396
MediaType mediaType = getContentType ().orElse (MediaType .APPLICATION_JSON_TYPE );
397
+
358
398
if (isFormSubmission (mediaType )) {
359
399
return getParametersFromBody (MapCollapseUtils .collapse (MapCollapseUtils .collapse (multi , single )));
360
400
} else {
0 commit comments