diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index d43fa8a516..0f25a3c360 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -902,8 +902,9 @@ async def _extract_form_body( value = serialize_sequence_value(field=field, value=results) if value is not None: values[field.alias] = value + field_aliases = {field.alias for field in body_fields} for key, value in received_body.items(): - if key not in values: + if key not in field_aliases: values[key] = value return values diff --git a/tests/test_form_default.py b/tests/test_form_default.py new file mode 100644 index 0000000000..2a12049d1a --- /dev/null +++ b/tests/test_form_default.py @@ -0,0 +1,35 @@ +from typing import Optional + +from fastapi import FastAPI, File, Form +from starlette.testclient import TestClient +from typing_extensions import Annotated + +app = FastAPI() + + +@app.post("/urlencoded") +async def post_url_encoded(age: Annotated[Optional[int], Form()] = None): + return age + + +@app.post("/multipart") +async def post_multi_part( + age: Annotated[Optional[int], Form()] = None, + file: Annotated[Optional[bytes], File()] = None, +): + return {"file": file, "age": age} + + +client = TestClient(app) + + +def test_form_default_url_encoded(): + response = client.post("/urlencoded", data={"age": ""}) + assert response.status_code == 200 + assert response.text == "null" + + +def test_form_default_multi_part(): + response = client.post("/multipart", data={"age": ""}) + assert response.status_code == 200 + assert response.json() == {"file": None, "age": None}