From 653533566e51d2346236d7e6f8ddfc95b8a0bf07 Mon Sep 17 00:00:00 2001 From: Omer Shamash <38649465+92dev@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:20:54 +0300 Subject: [PATCH] Allow streamable response classes Basically classes such as `StreamingResponse` in `starlette` cannot work unless specified directly in the response-type due to this serialization concept (which they always fallback to use JSON) There is the alternative which I opted out of for now, which is pass a `response_serialize: bool` parameter throughout the entire flow, it might be better but I wanted to avoid API changes (even through defaults) until any maintainer gives his opinion on the change example usage: ``` # Here `response_class` is only used for documentation @app.get('/test_before', response_class=StreamingResponse) async def before_fix(msg: str): async def txt_generator(): for i in range(10): yield f'event: something\ndata: {i}\n\n' return StreamingResponse(txt_generator()) # Here `response_class` is actually working @app.get('/test_after', response_class=StreamingResponse) async def after_fix(msg: str): async def txt_generator(): for i in range(10): yield f'event: something\ndata: {i}\n\n' returntxt_generator() ``` --- fastapi/routing.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/fastapi/routing.py b/fastapi/routing.py index fa1351859..825eda9bf 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -293,17 +293,19 @@ def get_request_handler( response_args["status_code"] = current_status_code if sub_response.status_code: response_args["status_code"] = sub_response.status_code - content = await serialize_response( - field=response_field, - response_content=raw_response, - include=response_model_include, - exclude=response_model_exclude, - by_alias=response_model_by_alias, - exclude_unset=response_model_exclude_unset, - exclude_defaults=response_model_exclude_defaults, - exclude_none=response_model_exclude_none, - is_coroutine=is_coroutine, - ) + # Some response classes handle serialization themselves via iteration + if not hasattr(actual_response_class, 'body_iterator'): + content = await serialize_response( + field=response_field, + response_content=raw_response, + include=response_model_include, + exclude=response_model_exclude, + by_alias=response_model_by_alias, + exclude_unset=response_model_exclude_unset, + exclude_defaults=response_model_exclude_defaults, + exclude_none=response_model_exclude_none, + is_coroutine=is_coroutine, + ) response = actual_response_class(content, **response_args) if not is_body_allowed_for_status_code(response.status_code): response.body = b""