]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
🐛 Fix parsing extra non-body parameter list (#14356)
authorMotov Yurii <109919500+YuriiMotov@users.noreply.github.com>
Tue, 2 Dec 2025 04:57:19 +0000 (05:57 +0100)
committerGitHub <noreply@github.com>
Tue, 2 Dec 2025 04:57:19 +0000 (05:57 +0100)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
fastapi/dependencies/utils.py
tests/test_query_cookie_header_model_extra_params.py [new file with mode: 0644]
tests/test_tutorial/test_header_param_models/test_tutorial003.py

index 2b2e6c5af97f74190aeca9d9111ff27139ce6c9a..20cb2c88c83f876a8e0efb23d7fba6ca9b590289 100644 (file)
@@ -791,9 +791,16 @@ def request_params_to_args(
         processed_keys.add(alias or field.alias)
         processed_keys.add(field.name)
 
-    for key, value in received_params.items():
+    for key in received_params.keys():
         if key not in processed_keys:
-            params_to_process[key] = value
+            if hasattr(received_params, "getlist"):
+                value = received_params.getlist(key)
+                if isinstance(value, list) and (len(value) == 1):
+                    params_to_process[key] = value[0]
+                else:
+                    params_to_process[key] = value
+            else:
+                params_to_process[key] = received_params.get(key)
 
     if single_not_embedded_field:
         field_info = first_field.field_info
diff --git a/tests/test_query_cookie_header_model_extra_params.py b/tests/test_query_cookie_header_model_extra_params.py
new file mode 100644 (file)
index 0000000..f4ebefb
--- /dev/null
@@ -0,0 +1,111 @@
+from fastapi import Cookie, FastAPI, Header, Query
+from fastapi._compat import PYDANTIC_V2
+from fastapi.testclient import TestClient
+from pydantic import BaseModel
+
+app = FastAPI()
+
+
+class Model(BaseModel):
+    param: str
+
+    if PYDANTIC_V2:
+        model_config = {"extra": "allow"}
+    else:
+
+        class Config:
+            extra = "allow"
+
+
+@app.get("/query")
+async def query_model_with_extra(data: Model = Query()):
+    return data
+
+
+@app.get("/header")
+async def header_model_with_extra(data: Model = Header()):
+    return data
+
+
+@app.get("/cookie")
+async def cookies_model_with_extra(data: Model = Cookie()):
+    return data
+
+
+def test_query_pass_extra_list():
+    client = TestClient(app)
+    resp = client.get(
+        "/query",
+        params={
+            "param": "123",
+            "param2": ["456", "789"],  # Pass a list of values as extra parameter
+        },
+    )
+    assert resp.status_code == 200
+    assert resp.json() == {
+        "param": "123",
+        "param2": ["456", "789"],
+    }
+
+
+def test_query_pass_extra_single():
+    client = TestClient(app)
+    resp = client.get(
+        "/query",
+        params={
+            "param": "123",
+            "param2": "456",
+        },
+    )
+    assert resp.status_code == 200
+    assert resp.json() == {
+        "param": "123",
+        "param2": "456",
+    }
+
+
+def test_header_pass_extra_list():
+    client = TestClient(app)
+
+    resp = client.get(
+        "/header",
+        headers=[
+            ("param", "123"),
+            ("param2", "456"),  # Pass a list of values as extra parameter
+            ("param2", "789"),
+        ],
+    )
+    assert resp.status_code == 200
+    resp_json = resp.json()
+    assert "param2" in resp_json
+    assert resp_json["param2"] == ["456", "789"]
+
+
+def test_header_pass_extra_single():
+    client = TestClient(app)
+
+    resp = client.get(
+        "/header",
+        headers=[
+            ("param", "123"),
+            ("param2", "456"),
+        ],
+    )
+    assert resp.status_code == 200
+    resp_json = resp.json()
+    assert "param2" in resp_json
+    assert resp_json["param2"] == "456"
+
+
+def test_cookie_pass_extra_list():
+    client = TestClient(app)
+    client.cookies = [
+        ("param", "123"),
+        ("param2", "456"),  # Pass a list of values as extra parameter
+        ("param2", "789"),
+    ]
+    resp = client.get("/cookie")
+    assert resp.status_code == 200
+    resp_json = resp.json()
+    assert "param2" in resp_json
+    assert resp_json["param2"] == "789"  # Cookies only keep the last value
index 60940e1da26ca386f4f8ea9e7be64781c800ac43..554a48d2e86475f65c0e96bef4ae1fe708d74005 100644 (file)
@@ -77,7 +77,7 @@ def test_header_param_model_no_underscore(client: TestClient):
                             "user-agent": "testclient",
                             "save-data": "true",
                             "if-modified-since": "yesterday",
-                            "x-tag": "two",
+                            "x-tag": ["one", "two"],
                         },
                     }
                 )