🐛 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 <yurii.motov.monte@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
ad hoc 2025-12-02 05:39:55 +01:00 committed by GitHub
parent c3373205d0
commit d68c066246
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 1 deletions

View File

@ -902,8 +902,9 @@ async def _extract_form_body(
value = serialize_sequence_value(field=field, value=results) value = serialize_sequence_value(field=field, value=results)
if value is not None: if value is not None:
values[field.alias] = value values[field.alias] = value
field_aliases = {field.alias for field in body_fields}
for key, value in received_body.items(): for key, value in received_body.items():
if key not in values: if key not in field_aliases:
values[key] = value values[key] = value
return values return values

View File

@ -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}