From 12bc9285f71c0c0f8eaf343764acd1d6f85c4317 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 14 Jun 2020 12:07:39 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20body=20validation=20error?= =?UTF-8?q?=20response,=20remove=20variable=20name=20when=20it=20is=20not?= =?UTF-8?q?=20embedded=20=20(#1553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/en/docs/tutorial/handling-errors.md | 1 - fastapi/dependencies/utils.py | 24 +++++++++++++------ tests/test_multi_body_errors.py | 10 ++++---- .../test_body/test_tutorial001.py | 14 ++++------- .../test_tutorial009.py | 2 +- .../test_tutorial002.py | 2 +- .../test_handling_errors/test_tutorial005.py | 2 +- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/docs/en/docs/tutorial/handling-errors.md b/docs/en/docs/tutorial/handling-errors.md index 19a6c5684..c5d36702c 100644 --- a/docs/en/docs/tutorial/handling-errors.md +++ b/docs/en/docs/tutorial/handling-errors.md @@ -215,7 +215,6 @@ You will receive a response telling you that the data is invalid containing the { "loc": [ "body", - "item", "size" ], "msg": "value is not a valid integer", diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index e7896f491..003fa7f4a 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -642,9 +642,17 @@ async def request_body_to_args( field = required_params[0] field_info = get_field_info(field) embed = getattr(field_info, "embed", None) - if len(required_params) == 1 and not embed: + field_alias_omitted = len(required_params) == 1 and not embed + if field_alias_omitted: received_body = {field.alias: received_body} + for field in required_params: + loc: Tuple[str, ...] + if field_alias_omitted: + loc = ("body",) + else: + loc = ("body", field.alias) + value: Any = None if received_body is not None: if ( @@ -655,7 +663,7 @@ async def request_body_to_args( try: value = received_body.get(field.alias) except AttributeError: - errors.append(get_missing_field_error(field.alias)) + errors.append(get_missing_field_error(loc)) continue if ( value is None @@ -667,7 +675,7 @@ async def request_body_to_args( ) ): if field.required: - errors.append(get_missing_field_error(field.alias)) + errors.append(get_missing_field_error(loc)) else: values[field.name] = deepcopy(field.default) continue @@ -686,7 +694,9 @@ async def request_body_to_args( awaitables = [sub_value.read() for sub_value in value] contents = await asyncio.gather(*awaitables) value = sequence_shape_to_type[field.shape](contents) - v_, errors_ = field.validate(value, values, loc=("body", field.alias)) + + v_, errors_ = field.validate(value, values, loc=loc) + if isinstance(errors_, ErrorWrapper): errors.append(errors_) elif isinstance(errors_, list): @@ -696,12 +706,12 @@ async def request_body_to_args( return values, errors -def get_missing_field_error(field_alias: str) -> ErrorWrapper: +def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper: if PYDANTIC_1: - missing_field_error = ErrorWrapper(MissingError(), loc=("body", field_alias)) + missing_field_error = ErrorWrapper(MissingError(), loc=loc) else: # pragma: no cover missing_field_error = ErrorWrapper( # type: ignore - MissingError(), loc=("body", field_alias), config=BaseConfig, + MissingError(), loc=loc, config=BaseConfig, ) return missing_field_error diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py index f19804261..4719f0b27 100644 --- a/tests/test_multi_body_errors.py +++ b/tests/test_multi_body_errors.py @@ -104,7 +104,7 @@ single_error = { "detail": [ { "ctx": {"limit_value": 0.0}, - "loc": ["body", "item", 0, "age"], + "loc": ["body", 0, "age"], "msg": "ensure this value is greater than 0", "type": "value_error.number.not_gt", } @@ -114,22 +114,22 @@ single_error = { multiple_errors = { "detail": [ { - "loc": ["body", "item", 0, "name"], + "loc": ["body", 0, "name"], "msg": "field required", "type": "value_error.missing", }, { - "loc": ["body", "item", 0, "age"], + "loc": ["body", 0, "age"], "msg": "value is not a valid decimal", "type": "type_error.decimal", }, { - "loc": ["body", "item", 1, "name"], + "loc": ["body", 1, "name"], "msg": "field required", "type": "value_error.missing", }, { - "loc": ["body", "item", 1, "age"], + "loc": ["body", 1, "age"], "msg": "value is not a valid decimal", "type": "type_error.decimal", }, diff --git a/tests/test_tutorial/test_body/test_tutorial001.py b/tests/test_tutorial/test_body/test_tutorial001.py index 806e712dc..df28a5d35 100644 --- a/tests/test_tutorial/test_body/test_tutorial001.py +++ b/tests/test_tutorial/test_body/test_tutorial001.py @@ -94,7 +94,7 @@ def test_openapi_schema(): price_missing = { "detail": [ { - "loc": ["body", "item", "price"], + "loc": ["body", "price"], "msg": "field required", "type": "value_error.missing", } @@ -104,7 +104,7 @@ price_missing = { price_not_float = { "detail": [ { - "loc": ["body", "item", "price"], + "loc": ["body", "price"], "msg": "value is not a valid float", "type": "type_error.float", } @@ -114,12 +114,12 @@ price_not_float = { name_price_missing = { "detail": [ { - "loc": ["body", "item", "name"], + "loc": ["body", "name"], "msg": "field required", "type": "value_error.missing", }, { - "loc": ["body", "item", "price"], + "loc": ["body", "price"], "msg": "field required", "type": "value_error.missing", }, @@ -128,11 +128,7 @@ name_price_missing = { body_missing = { "detail": [ - { - "loc": ["body", "item"], - "msg": "field required", - "type": "value_error.missing", - } + {"loc": ["body"], "msg": "field required", "type": "value_error.missing",} ] } diff --git a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py index 8a9f39533..55859eb2b 100644 --- a/tests/test_tutorial/test_body_nested_models/test_tutorial009.py +++ b/tests/test_tutorial/test_body_nested_models/test_tutorial009.py @@ -95,7 +95,7 @@ def test_post_invalid_body(): assert response.json() == { "detail": [ { - "loc": ["body", "weights", "__key__"], + "loc": ["body", "__key__"], "msg": "value is not a valid integer", "type": "type_error.integer", } diff --git a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py index 59daaf73b..170065f64 100644 --- a/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py +++ b/tests/test_tutorial/test_custom_request_and_route/test_tutorial002.py @@ -18,7 +18,7 @@ def test_exception_handler_body_access(): "body": '{"numbers": [1, 2, 3]}', "errors": [ { - "loc": ["body", "numbers"], + "loc": ["body"], "msg": "value is not a valid list", "type": "type_error.list", } diff --git a/tests/test_tutorial/test_handling_errors/test_tutorial005.py b/tests/test_tutorial/test_handling_errors/test_tutorial005.py index f974466d1..083f5141c 100644 --- a/tests/test_tutorial/test_handling_errors/test_tutorial005.py +++ b/tests/test_tutorial/test_handling_errors/test_tutorial005.py @@ -92,7 +92,7 @@ def test_post_validation_error(): assert response.json() == { "detail": [ { - "loc": ["body", "item", "size"], + "loc": ["body", "size"], "msg": "value is not a valid integer", "type": "type_error.integer", }