diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 84dfa4d03..603dfa231 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -845,6 +845,7 @@ async def _extract_form_body( values = {} first_field = body_fields[0] first_field_info = first_field.field_info + processed_keys = set() for field in body_fields: value = _get_multidict_value(field, received_body) @@ -873,10 +874,11 @@ async def _extract_form_body( for sub_value in value: tg.start_soon(process_fn, sub_value.read) value = serialize_sequence_value(field=field, value=results) + processed_keys.add(field.alias) if value is not None: values[field.alias] = value for key, value in received_body.items(): - if key not in values: + if key not in processed_keys: values[key] = value return values diff --git a/tests/test_form_default.py b/tests/test_form_default.py new file mode 100644 index 000000000..97ff47463 --- /dev/null +++ b/tests/test_form_default.py @@ -0,0 +1,47 @@ +from typing import Optional + +from fastapi import FastAPI, File, Form +from starlette.testclient import TestClient +from typing_extensions import Annotated + +app = FastAPI() +client = TestClient(app) + + +@app.post("/urlencoded") +async def post_url_encoded(age: Annotated[Optional[int], Form()] = None): + return age + + +def test_form_default_url_encoded(): + response = client.post("/urlencoded", data={"age": ""}) + assert response.status_code == 200 + assert response.text == "null" + + +@app.post("/multipart") +async def post_multi_part( + age: Annotated[Optional[int], Form()] = None, + file: Annotated[Optional[bytes], File()] = None, +): + assert file is None + assert age is None + + +def test_form_default_multi_part(): + response = client.post("/multipart", data={"age": ""}) + assert response.status_code == 200 + + +@app.post("/multipart-file-first") +async def post_multi_part_file_first( + file: Annotated[Optional[bytes], File()] = None, + age: Annotated[Optional[int], Form()] = None, +): + assert file is None + assert age is None + + +def test_form_default_multi_part_file_first(): + response = client.post("/multipart-file-first", data={"age": ""}) + assert response.status_code == 200