Skip to content

Commit d29647b

Browse files
committed
Fixed Falcon multipart
1 parent fe1b693 commit d29647b

File tree

1 file changed

+48
-25
lines changed

1 file changed

+48
-25
lines changed

fastopenapi/routers/falcon/extractors.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
BaseRequestDataExtractor,
99
)
1010

11+
_MULTIPART_CACHE_ATTR = "_fastopenapi_multipart_cache"
12+
1113

1214
class FalconRequestDataExtractor(BaseRequestDataExtractor):
1315
@classmethod
@@ -51,49 +53,70 @@ def _get_body(cls, request: Any) -> dict | list | None:
5153
return {}
5254

5355
@classmethod
54-
def _get_form_data(cls, request: Any) -> dict:
55-
"""Extract form data"""
56-
form_data = {}
57-
ct = (request.content_type or "").lower()
56+
def _parse_multipart(
57+
cls, request: Any
58+
) -> tuple[dict, dict[str, FileUpload | list[FileUpload]]]:
59+
"""Parse multipart stream once and cache the result on the request."""
60+
cached = getattr(request, _MULTIPART_CACHE_ATTR, None)
61+
if isinstance(cached, tuple):
62+
return cached
5863

59-
if ct == "application/x-www-form-urlencoded":
60-
form_data = request.media or {}
61-
62-
return form_data
63-
64-
@classmethod
65-
def _get_files(cls, request: Any) -> dict[str, FileUpload | list[FileUpload]]:
66-
"""Extract files from Falcon request (sync)"""
67-
files = {}
68-
ct = (request.content_type or "").lower()
64+
form_data = {}
65+
files: dict[str, FileUpload | list[FileUpload]] = {}
6966

70-
if "multipart/form-data" in ct and hasattr(request, "get_media"):
71-
form = request.get_media()
72-
for part in form:
73-
filename = getattr(part, "secure_filename", None) or getattr(
74-
part, "filename", None
75-
)
76-
if not filename:
77-
continue
67+
form = request.get_media()
68+
for part in form:
69+
filename = getattr(part, "secure_filename", None) or getattr(
70+
part, "filename", None
71+
)
72+
field_name = getattr(part, "name", filename)
7873

74+
if filename:
7975
content = part.stream.read()
8076
file_upload = FileUpload(
8177
filename=filename,
8278
content_type=getattr(part, "content_type", None),
8379
size=len(content),
8480
file=content,
8581
)
86-
87-
field_name = getattr(part, "name", filename)
8882
if field_name in files:
8983
if isinstance(files[field_name], list):
9084
files[field_name].append(file_upload)
9185
else:
9286
files[field_name] = [files[field_name], file_upload]
9387
else:
9488
files[field_name] = file_upload
89+
else:
90+
form_data[field_name] = part.text
91+
92+
result = (form_data, files)
93+
setattr(request, _MULTIPART_CACHE_ATTR, result)
94+
return result
9595

96-
return files
96+
@classmethod
97+
def _get_form_data(cls, request: Any) -> dict:
98+
"""Extract form data"""
99+
ct = str(request.content_type or "").lower()
100+
101+
if ct == "application/x-www-form-urlencoded":
102+
return request.media or {}
103+
104+
if "multipart/form-data" in ct and hasattr(request, "get_media"):
105+
form_data, _ = cls._parse_multipart(request)
106+
return form_data
107+
108+
return {}
109+
110+
@classmethod
111+
def _get_files(cls, request: Any) -> dict[str, FileUpload | list[FileUpload]]:
112+
"""Extract files from Falcon request (sync)"""
113+
ct = str(request.content_type or "").lower()
114+
115+
if "multipart/form-data" in ct and hasattr(request, "get_media"):
116+
_, files = cls._parse_multipart(request)
117+
return files
118+
119+
return {}
97120

98121

99122
class FalconAsyncRequestDataExtractor(

0 commit comments

Comments
 (0)