import asyncio
+import email.message
import enum
import inspect
import json
)
from pydantic import BaseModel
from pydantic.error_wrappers import ErrorWrapper, ValidationError
-from pydantic.fields import ModelField
+from pydantic.fields import ModelField, Undefined
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
async def app(request: Request) -> Response:
try:
- body = None
+ body: Any = None
if body_field:
if is_body_form:
body = await request.form()
else:
body_bytes = await request.body()
if body_bytes:
- body = await request.json()
+ json_body: Any = Undefined
+ content_type_value = request.headers.get("content-type")
+ if content_type_value:
+ message = email.message.Message()
+ message["content-type"] = content_type_value
+ if message.get_content_maintype() == "application":
+ subtype = message.get_content_subtype()
+ if subtype == "json" or subtype.endswith("+json"):
+ json_body = await request.json()
+ if json_body != Undefined:
+ body = json_body
+ else:
+ body = body_bytes
except json.JSONDecodeError as e:
raise RequestValidationError([ErrorWrapper(e, ("body", e.pos))], body=e.doc)
except Exception as e:
def test_post_broken_body():
- response = client.post("/items/", data={"name": "Foo", "price": 50.5})
+ response = client.post(
+ "/items/",
+ headers={"content-type": "application/json"},
+ data="{some broken json}",
+ )
assert response.status_code == 422, response.text
assert response.json() == {
"detail": [
{
+ "loc": ["body", 1],
+ "msg": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
+ "type": "value_error.jsondecode",
"ctx": {
- "colno": 1,
- "doc": "name=Foo&price=50.5",
+ "msg": "Expecting property name enclosed in double quotes",
+ "doc": "{some broken json}",
+ "pos": 1,
"lineno": 1,
- "msg": "Expecting value",
- "pos": 0,
+ "colno": 2,
},
- "loc": ["body", 0],
- "msg": "Expecting value: line 1 column 1 (char 0)",
- "type": "value_error.jsondecode",
}
]
}
+
+
+def test_post_form_for_json():
+ response = client.post("/items/", data={"name": "Foo", "price": 50.5})
+ assert response.status_code == 422, response.text
+ assert response.json() == {
+ "detail": [
+ {
+ "loc": ["body"],
+ "msg": "value is not a valid dict",
+ "type": "type_error.dict",
+ }
+ ]
+ }
+
+
+def test_explicit_content_type():
+ response = client.post(
+ "/items/",
+ data='{"name": "Foo", "price": 50.5}',
+ headers={"Content-Type": "application/json"},
+ )
+ assert response.status_code == 200, response.text
+
+
+def test_geo_json():
+ response = client.post(
+ "/items/",
+ data='{"name": "Foo", "price": 50.5}',
+ headers={"Content-Type": "application/geo+json"},
+ )
+ assert response.status_code == 200, response.text
+
+
+def test_wrong_headers():
+ data = '{"name": "Foo", "price": 50.5}'
+ invalid_dict = {
+ "detail": [
+ {
+ "loc": ["body"],
+ "msg": "value is not a valid dict",
+ "type": "type_error.dict",
+ }
+ ]
+ }
+
+ response = client.post("/items/", data=data, headers={"Content-Type": "text/plain"})
+ assert response.status_code == 422, response.text
+ assert response.json() == invalid_dict
+
+ response = client.post(
+ "/items/", data=data, headers={"Content-Type": "application/geo+json-seq"}
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == invalid_dict
+ response = client.post(
+ "/items/", data=data, headers={"Content-Type": "application/not-really-json"}
+ )
+ assert response.status_code == 422, response.text
+ assert response.json() == invalid_dict
+
+
+def test_other_exceptions():
with patch("json.loads", side_effect=Exception):
response = client.post("/items/", json={"test": "test2"})
assert response.status_code == 400, response.text
- assert response.json() == {"detail": "There was an error parsing the body"}