From: ad hoc Date: Tue, 2 Dec 2025 04:39:55 +0000 (+0100) Subject: 🐛 Fix support for form values with empty strings interpreted as missing (`None` if... X-Git-Tag: 0.123.2~14 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d68c066246c9a70f179ca165614dac19b8c8beeb;p=thirdparty%2Ffastapi%2Ffastapi.git 🐛 Fix support for form values with empty strings interpreted as missing (`None` if that's the default), for compatibility with HTML forms (#13537) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Motov Yurii <109919500+YuriiMotov@users.noreply.github.com> Co-authored-by: Yurii Motov Co-authored-by: Sebastián Ramírez --- 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}