Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 19 additions & 61 deletions docs/en/docs/advanced/custom-response.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others }

By default, **FastAPI** will return the responses using `JSONResponse`.
By default, **FastAPI** will return JSON responses.

You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.

Expand All @@ -10,43 +10,27 @@ But you can also declare the `Response` that you want to be used (e.g. any `Resp

The contents that you return from your *path operation function* will be put inside of that `Response`.

And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.

/// note

If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.

///

## Use `ORJSONResponse` { #use-orjsonresponse }

For example, if you are squeezing performance, you can install and use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> and set the response to be `ORJSONResponse`.

Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
## JSON Responses { #json-responses }

For large responses, returning a `Response` directly is much faster than returning a dictionary.
By default FastAPI returns JSON responses.

This is because by default, FastAPI will inspect every item inside and make sure it is serializable as JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models.
If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.

But if you are certain that the content that you are returning is **serializable with JSON**, you can pass it directly to the response class and avoid the extra overhead that FastAPI would have by passing your return content through the `jsonable_encoder` before passing it to the response class.

{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}

/// info

The parameter `response_class` will also be used to define the "media type" of the response.

In this case, the HTTP header `Content-Type` will be set to `application/json`.

And it will be documented as such in OpenAPI.
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.

///
If you declare a `response_class` with a JSON media type (`application/json`), like is the case with the `JSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. But the data won't be serialized to JSON bytes with Pydantic, instead it will be converted with the `jsonable_encoder` and then passed to the `JSONResponse` class, which will serialize it to bytes using the standard JSON library in Python.

/// tip
### JSON Performance { #json-performance }

The `ORJSONResponse` is only available in FastAPI, not in Starlette.
In short, if you want the maximum performance, use a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} and don't declare a `response_class` in the *path operation decorator*.

///
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}

## HTML Response { #html-response }

Expand Down Expand Up @@ -154,40 +138,6 @@ Takes some data and returns an `application/json` encoded response.

This is the default response used in **FastAPI**, as you read above.

### `ORJSONResponse` { #orjsonresponse }

A fast alternative JSON response using <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, as you read above.

/// info

This requires installing `orjson` for example with `pip install orjson`.

///

### `UJSONResponse` { #ujsonresponse }

An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.

/// info

This requires installing `ujson` for example with `pip install ujson`.

///

/// warning

`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.

///

{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}

/// tip

It's possible that `ORJSONResponse` might be a faster alternative.

///

### `RedirectResponse` { #redirectresponse }

Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
Expand Down Expand Up @@ -268,7 +218,7 @@ In this case, you can return the file path directly from your *path operation* f

You can create your own custom response class, inheriting from `Response` and using it.

For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, but with some custom settings not used in the included `ORJSONResponse` class.
For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> with some settings.

Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`.

Expand All @@ -292,13 +242,21 @@ Now instead of returning:

Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉

### `orjson` or Response Model { #orjson-or-response-model }

If what you are looking for is performance, you are probably better off using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than an `orjson` response.

With a response model, FastAPI will use Pydantic to serialize the data to JSON, without using intermediate steps, like converting it with `jsonable_encoder`, which would happen in any other case.

And under the hood, Pydantic uses the same underlying Rust mechanisms as `orjson` to serialize to JSON, so you will already get the best performance with a response model.

## Default response class { #default-response-class }

When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.

The parameter that defines this is `default_response_class`.

In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
In the example below, **FastAPI** will use `HTMLResponse` by default, in all *path operations*, instead of JSON.

{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}

Expand Down
28 changes: 22 additions & 6 deletions docs/en/docs/advanced/response-directly.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.

By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.

Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client.
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.

But you can return a `JSONResponse` directly from your *path operations*.
You could also create a `JSONResponse` directly and return it.

It might be useful, for example, to return custom headers or cookies.
/// tip

You will normally have much better performance using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than returning a `JSONResponse` directly, as that way it serializes the data using Pydantic, in Rust.

///

## Return a `Response` { #return-a-response }

In fact, you can return any `Response` or any sub-class of it.
You can return any `Response` or any sub-class of it.

/// tip
/// info

`JSONResponse` itself is a sub-class of `Response`.

Expand Down Expand Up @@ -56,6 +60,18 @@ You could put your XML content in a string, put that in a `Response`, and return

{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}

## How a Response Model Works { #how-a-response-model-works }

When you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} in a path operation, **FastAPI** will use it to serialize the data to JSON, using Pydantic.

{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}

As that will happen on the Rust side, the performance will be much better than if it was done with regular Python and the `JSONResponse` class.

When using a response model FastAPI won't use the `jsonable_encoder` to convert the data (which would be slower) nor the `JSONResponse` class.

Instead it takes the JSON bytes generated with Pydantic using the response model and returns a `Response` with the right media type for JSON directly (`application/json`).

## Notes { #notes }

When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.
Expand Down
4 changes: 4 additions & 0 deletions docs/en/docs/how-to/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Here are several pointers to other places in the docs, for general or frequent q

To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.

## Optimize Response Performance - Response Model - Return Type { #optimize-response-performance-response-model-return-type }

To optimize performance when returning JSON data, use a return type or response model, that way Pydantic will handle the serialization to JSON on the Rust side, without going through Python. Read more in the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.

## Documentation Tags - OpenAPI { #documentation-tags-openapi }

To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
Expand Down
1 change: 1 addition & 0 deletions docs/en/docs/tutorial/response-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ FastAPI will use this return type to:
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
* This will be used by the **automatic docs**.
* It will also be used by automatic client code generation tools.
* **Serialize** the returned data to JSON using Pydantic, which is written in **Rust**, so it will be **much faster**.

But most importantly:

Expand Down
6 changes: 3 additions & 3 deletions docs_src/custom_response/tutorial010_py310.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse
from fastapi.responses import HTMLResponse

app = FastAPI(default_response_class=ORJSONResponse)
app = FastAPI(default_response_class=HTMLResponse)


@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
return "<h1>Items</h1><p>This is a list of items.</p>"
26 changes: 26 additions & 0 deletions fastapi/_compat/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,32 @@ def serialize(
exclude_none=exclude_none,
)

def serialize_json(
self,
value: Any,
*,
include: IncEx | None = None,
exclude: IncEx | None = None,
by_alias: bool = True,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> bytes:
# What calls this code passes a value that already called
# self._type_adapter.validate_python(value)
# This uses Pydantic's dump_json() which serializes directly to JSON
# bytes in one pass (via Rust), avoiding the intermediate Python dict
# step of dump_python(mode="json") + json.dumps().
return self._type_adapter.dump_json(
value,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)

def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from
# ModelField to its JSON Schema.
Expand Down
23 changes: 20 additions & 3 deletions fastapi/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ async def serialize_response(
exclude_none: bool = False,
is_coroutine: bool = True,
endpoint_ctx: EndpointContext | None = None,
dump_json: bool = False,
) -> Any:
if field:
if is_coroutine:
Expand All @@ -286,8 +287,8 @@ async def serialize_response(
body=response_content,
endpoint_ctx=ctx,
)

return field.serialize(
serializer = field.serialize_json if dump_json else field.serialize
return serializer(
value,
include=include,
exclude=exclude,
Expand Down Expand Up @@ -443,6 +444,14 @@ async def app(request: Request) -> Response:
response_args["status_code"] = current_status_code
if solved_result.response.status_code:
response_args["status_code"] = solved_result.response.status_code
# Use the fast path (dump_json) when no custom response
# class was set and a response field with a TypeAdapter
# exists. Serializes directly to JSON bytes via Pydantic's
# Rust core, skipping the intermediate Python dict +
# json.dumps() step.
use_dump_json = response_field is not None and isinstance(
response_class, DefaultPlaceholder
)
content = await serialize_response(
field=response_field,
response_content=raw_response,
Expand All @@ -454,8 +463,16 @@ async def app(request: Request) -> Response:
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
endpoint_ctx=endpoint_ctx,
dump_json=use_dump_json,
)
response = actual_response_class(content, **response_args)
if use_dump_json:
response = Response(
content=content,
media_type="application/json",
**response_args,
)
else:
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
response.body = b""
response.headers.raw.extend(solved_result.response.headers.raw)
Expand Down
51 changes: 51 additions & 0 deletions tests/test_dump_json_fast_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from unittest.mock import patch

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
from pydantic import BaseModel


class Item(BaseModel):
name: str
price: float


app = FastAPI()


@app.get("/default")
def get_default() -> Item:
return Item(name="widget", price=9.99)


@app.get("/explicit", response_class=JSONResponse)
def get_explicit() -> Item:
return Item(name="widget", price=9.99)


client = TestClient(app)


def test_default_response_class_skips_json_dumps():
"""When no response_class is set, the fast path serializes directly to
JSON bytes via Pydantic's dump_json and never calls json.dumps."""
with patch(
"starlette.responses.json.dumps", wraps=__import__("json").dumps
) as mock_dumps:
response = client.get("/default")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
mock_dumps.assert_not_called()


def test_explicit_response_class_uses_json_dumps():
"""When response_class is explicitly set to JSONResponse, the normal path
is used and json.dumps is called via JSONResponse.render()."""
with patch(
"starlette.responses.json.dumps", wraps=__import__("json").dumps
) as mock_dumps:
response = client.get("/explicit")
assert response.status_code == 200
assert response.json() == {"name": "widget", "price": 9.99}
mock_dumps.assert_called_once()
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
name="client",
params=[
pytest.param("tutorial001_py310"),
pytest.param("tutorial010_py310"),
],
)
def get_client(request: pytest.FixtureRequest):
Expand Down
Loading