mirror of
https://github.com/fastapi/fastapi.git
synced 2026-02-12 07:11:05 -05:00
Compare commits
40 Commits
0.128.5
...
docs-py310
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ddcca6b9a | ||
|
|
af77d9b718 | ||
|
|
400ccb04aa | ||
|
|
fbfc1722e5 | ||
|
|
d06ab3f5c7 | ||
|
|
3da206c06d | ||
|
|
cc903bd440 | ||
|
|
ad4e8e0060 | ||
|
|
bdd20051c4 | ||
|
|
1ed9bd4923 | ||
|
|
aac30fd707 | ||
|
|
417f1ee078 | ||
|
|
ffb8965260 | ||
|
|
93fa935fb8 | ||
|
|
f0f3e7a113 | ||
|
|
8f82c94de0 | ||
|
|
5bb3423205 | ||
|
|
6ce5e3e961 | ||
|
|
65da3dde12 | ||
|
|
81f82fd955 | ||
|
|
ff721017df | ||
|
|
ca76a4eba9 | ||
|
|
1133a4594d | ||
|
|
38f965985e | ||
|
|
3f1cc8f8f5 | ||
|
|
25270fcee0 | ||
|
|
8bdb0d2242 | ||
|
|
df950111fe | ||
|
|
363aced75a | ||
|
|
66dc695071 | ||
|
|
e94028ab60 | ||
|
|
8fd291465b | ||
|
|
fbca586c1d | ||
|
|
4e879799dd | ||
|
|
0a4033aeee | ||
|
|
ed2512a5ec | ||
|
|
0c0f6332e2 | ||
|
|
227cb85a03 | ||
|
|
cd31576d57 | ||
|
|
376e108580 |
9
.github/workflows/test-redistribute.yml
vendored
9
.github/workflows/test-redistribute.yml
vendored
@@ -12,11 +12,6 @@ on:
|
||||
jobs:
|
||||
test-redistribute:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- fastapi
|
||||
- fastapi-slim
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
@@ -30,8 +25,6 @@ jobs:
|
||||
- name: Install build dependencies
|
||||
run: pip install build
|
||||
- name: Build source distribution
|
||||
env:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
run: python -m build --sdist
|
||||
- name: Decompress source distribution
|
||||
run: |
|
||||
@@ -41,8 +34,6 @@ jobs:
|
||||
run: |
|
||||
cd dist/fastapi*/
|
||||
pip install --group tests --editable .[all]
|
||||
env:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
- name: Run source distribution tests
|
||||
run: |
|
||||
cd dist/fastapi*/
|
||||
|
||||
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@@ -14,6 +14,7 @@ on:
|
||||
|
||||
env:
|
||||
UV_NO_SYNC: true
|
||||
INLINE_SNAPSHOT_DEFAULT_FLAGS: review
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -55,14 +56,10 @@ jobs:
|
||||
- starlette-pypi
|
||||
- starlette-git
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.9"
|
||||
coverage: coverage
|
||||
uv-resolution: lowest-direct
|
||||
- os: macos-latest
|
||||
python-version: "3.10"
|
||||
coverage: coverage
|
||||
uv-resolution: highest
|
||||
uv-resolution: lowest-direct
|
||||
- os: windows-latest
|
||||
python-version: "3.12"
|
||||
coverage: coverage
|
||||
|
||||
10
README.md
10
README.md
@@ -34,7 +34,7 @@ The key features are:
|
||||
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
|
||||
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
|
||||
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
|
||||
* **Intuitive**: Great editor support. <dfn title="also known as auto-complete, autocompletion, IntelliSense">Completion</dfn> everywhere. Less time debugging.
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
@@ -371,7 +371,7 @@ item: Item
|
||||
* Validation of data:
|
||||
* Automatic and clear errors when the data is invalid.
|
||||
* Validation even for deeply nested JSON objects.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
|
||||
* <dfn title="also known as: serialization, parsing, marshalling">Conversion</dfn> of input data: coming from the network to Python data and types. Reading from:
|
||||
* JSON.
|
||||
* Path parameters.
|
||||
* Query parameters.
|
||||
@@ -379,7 +379,7 @@ item: Item
|
||||
* Headers.
|
||||
* Forms.
|
||||
* Files.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
|
||||
* <dfn title="also known as: serialization, parsing, marshalling">Conversion</dfn> of output data: converting from Python data and types to network data (as JSON):
|
||||
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* `datetime` objects.
|
||||
* `UUID` objects.
|
||||
@@ -442,7 +442,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
|
||||
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
|
||||
* How to set **validation constraints** as `maximum_length` or `regex`.
|
||||
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
* A very powerful and easy to use **<dfn title="also known as components, resources, providers, services, injectables">Dependency Injection</dfn>** system.
|
||||
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
@@ -527,7 +527,7 @@ Used by Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <dfn title="converting the string that comes from an HTTP request into Python data">"parsing"</dfn>, with `request.form()`.
|
||||
|
||||
Used by FastAPI:
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Siehe Abschnitt `### Content of code snippets` im allgemeinen Prompt in `scripts
|
||||
|
||||
//// tab | Test
|
||||
|
||||
Gestern schrieb mein Freund: „Wenn man unkorrekt korrekt schreibt, hat man es unkorrekt geschrieben“. Worauf ich antwortete: „Korrekt, aber ‚unkorrekt‘ ist unkorrekterweise nicht ‚„unkorrekt“‘“.
|
||||
Gestern schrieb mein Freund: „Wenn man ‚incorrectly‘ korrekt schreibt, hat man es falsch geschrieben“. Worauf ich antwortete: „Korrekt, aber ‚incorrectly‘ ist inkorrekterweise nicht ‚„incorrectly“‘“.
|
||||
|
||||
/// note | Hinweis
|
||||
|
||||
@@ -202,11 +202,6 @@ Hier einige Dinge, die in HTML-„abbr“-Elemente gepackt sind (einige sind erf
|
||||
* <abbr title="XML Web Token">XWT</abbr>
|
||||
* <abbr title="Paralleles Server-Gateway-Interface">PSGI</abbr>
|
||||
|
||||
### Das abbr gibt eine Erklärung { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und in irgendeiner Weise zusammenarbeiten.">Cluster</abbr>
|
||||
* <abbr title="Eine Methode des Machine Learning, die künstliche neuronale Netze mit zahlreichen versteckten Schichten zwischen Eingabe- und Ausgabeschicht verwendet und so eine umfassende interne Struktur entwickelt">Deep Learning</abbr>
|
||||
|
||||
### Das abbr gibt eine vollständige Phrase und eine Erklärung { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network – Mozilla-Entwicklernetzwerk: Dokumentation für Entwickler, geschrieben von den Firefox-Leuten">MDN</abbr>
|
||||
@@ -224,6 +219,11 @@ Siehe Abschnitt `### HTML abbr elements` im allgemeinen Prompt in `scripts/trans
|
||||
|
||||
////
|
||||
|
||||
## HTML „dfn“-Elemente { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Eine Gruppe von Maschinen, die so konfiguriert sind, dass sie verbunden sind und in irgendeiner Weise zusammenarbeiten.">Cluster</dfn>
|
||||
* <dfn title="Eine Methode des Machine Learning, die künstliche neuronale Netze mit zahlreichen versteckten Schichten zwischen Eingabe- und Ausgabeschicht verwendet und so eine umfassende interne Struktur entwickelt">Deep Learning</dfn>
|
||||
|
||||
## Überschriften { #headings }
|
||||
|
||||
//// tab | Test
|
||||
@@ -248,7 +248,7 @@ Die einzige strenge Regel für Überschriften ist, dass das LLM den Hash-Teil in
|
||||
|
||||
Siehe Abschnitt `### Headings` im allgemeinen Prompt in `scripts/translate.py`.
|
||||
|
||||
Für einige sprachspezifische Anweisungen, siehe z. B. den Abschnitt `### Headings` in `docs/de/llm-prompt.md`.
|
||||
Für einige sprachsspezifische Anweisungen, siehe z. B. den Abschnitt `### Headings` in `docs/de/llm-prompt.md`.
|
||||
|
||||
////
|
||||
|
||||
|
||||
@@ -202,11 +202,6 @@ Here some things wrapped in HTML "abbr" elements (Some are invented):
|
||||
* <abbr title="XML Web Token">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface">PSGI</abbr>
|
||||
|
||||
### The abbr gives an explanation { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr>
|
||||
* <abbr title="A method of machine learning that uses artificial neural networks with numerous hidden layers between input and output layers, thereby developing a comprehensive internal structure">Deep Learning</abbr>
|
||||
|
||||
### The abbr gives a full phrase and an explanation { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network: documentation for developers, written by the Firefox people">MDN</abbr>
|
||||
@@ -224,6 +219,11 @@ See section `### HTML abbr elements` in the general prompt in `scripts/translate
|
||||
|
||||
////
|
||||
|
||||
## HTML "dfn" elements { #html-dfn-elements }
|
||||
|
||||
* <dfn title="A group of machines that are configured to be connected and work together in some way.">cluster</dfn>
|
||||
* <dfn title="A method of machine learning that uses artificial neural networks with numerous hidden layers between input and output layers, thereby developing a comprehensive internal structure">Deep Learning</dfn>
|
||||
|
||||
## Headings { #headings }
|
||||
|
||||
//// tab | Test
|
||||
|
||||
@@ -26,7 +26,7 @@ Each of those response `dict`s can have a key `model`, containing a Pydantic mod
|
||||
|
||||
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *}
|
||||
|
||||
/// note
|
||||
|
||||
@@ -203,7 +203,7 @@ For example, you can declare a response with a status code `404` that uses a Pyd
|
||||
|
||||
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *}
|
||||
|
||||
It will all be combined and included in your OpenAPI, and shown in the API docs:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Not the class itself (which is already a callable), but an instance of that clas
|
||||
|
||||
To do that, we declare a method `__call__`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *}
|
||||
|
||||
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
|
||||
|
||||
@@ -26,7 +26,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
|
||||
|
||||
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *}
|
||||
|
||||
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
|
||||
|
||||
@@ -34,7 +34,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
|
||||
|
||||
We could create an instance of this class with:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *}
|
||||
|
||||
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
|
||||
|
||||
@@ -50,7 +50,7 @@ checker(q="somequery")
|
||||
|
||||
...and pass whatever that returns as the value of the dependency in our *path operation function* as the parameter `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
61
docs/en/docs/advanced/advanced-python-types.md
Normal file
61
docs/en/docs/advanced/advanced-python-types.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Advanced Python Types { #advanced-python-types }
|
||||
|
||||
Here are some additional ideas that might be useful when working with Python types.
|
||||
|
||||
## Using `Union` or `Optional` { #using-union-or-optional }
|
||||
|
||||
If your code for some reason can't use `|`, for example if it's not in a type annotation but in something like `response_model=`, instead of using the vertical bar (`|`) you can use `Union` from `typing`.
|
||||
|
||||
For example, you could declare that something could be a `str` or `None`:
|
||||
|
||||
```python
|
||||
from typing import Union
|
||||
|
||||
|
||||
def say_hi(name: Union[str, None]):
|
||||
print(f"Hi {name}!")
|
||||
```
|
||||
|
||||
`typing` also has a shortcut to declare that something could be `None`, with `Optional`.
|
||||
|
||||
Here's a tip from my very **subjective** point of view:
|
||||
|
||||
* 🚨 Avoid using `Optional[SomeType]`
|
||||
* Instead ✨ **use `Union[SomeType, None]`** ✨.
|
||||
|
||||
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
|
||||
|
||||
I think `Union[SomeType, None]` is more explicit about what it means.
|
||||
|
||||
It's just about the words and names. But those words can affect how you and your teammates think about the code.
|
||||
|
||||
As an example, let's take this function:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def say_hi(name: Optional[str]):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, no, this throws an error! 😱
|
||||
```
|
||||
|
||||
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # This works, None is valid 🎉
|
||||
```
|
||||
|
||||
The good news is, in most cases, you will be able to simply use `|` to define unions of types:
|
||||
|
||||
```python
|
||||
def say_hi(name: str | None):
|
||||
print(f"Hey {name}!")
|
||||
```
|
||||
|
||||
So, normally you don't have to worry about names like `Optional` and `Union`. 😎
|
||||
@@ -32,11 +32,11 @@ For a simple example, let's consider a file structure similar to the one describ
|
||||
|
||||
The file `main.py` would have:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/main.py *}
|
||||
|
||||
The file `test_main.py` would have the tests for `main.py`, it could look like this now:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py *}
|
||||
|
||||
## Run it { #run-it }
|
||||
|
||||
@@ -56,7 +56,7 @@ $ pytest
|
||||
|
||||
The marker `@pytest.mark.anyio` tells pytest that this test function should be called asynchronously:
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -66,7 +66,7 @@ Note that the test function is now `async def` instead of just `def` as before w
|
||||
|
||||
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}
|
||||
|
||||
This is the equivalent to:
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ $ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
For example, let's say you define a *path operation* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
|
||||
|
||||
If the client tries to go to `/items`, by default, it would be redirected to `/items/`.
|
||||
|
||||
@@ -115,7 +115,7 @@ In this case, the original path `/app` would actually be served at `/api/v1/app`
|
||||
|
||||
Even though all your code is written assuming there's just `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
|
||||
|
||||
And the proxy would be **"stripping"** the **path prefix** on the fly before transmitting the request to the app server (probably Uvicorn via FastAPI CLI), keeping your application convinced that it is being served at `/app`, so that you don't have to update all your code to include the prefix `/api/v1`.
|
||||
|
||||
@@ -193,7 +193,7 @@ You can get the current `root_path` used by your application for each request, i
|
||||
|
||||
Here we are including it in the message just for demonstration purposes.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
|
||||
|
||||
Then, if you start Uvicorn with:
|
||||
|
||||
@@ -220,7 +220,7 @@ The response would be something like:
|
||||
|
||||
Alternatively, if you don't have a way to provide a command line option like `--root-path` or equivalent, you can set the `root_path` parameter when creating your FastAPI app:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
|
||||
|
||||
Passing the `root_path` to `FastAPI` would be the equivalent of passing the `--root-path` command line option to Uvicorn or Hypercorn.
|
||||
|
||||
@@ -400,7 +400,7 @@ If you pass a custom list of `servers` and there's a `root_path` (because your A
|
||||
|
||||
For example:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
|
||||
|
||||
Will generate an OpenAPI schema like:
|
||||
|
||||
@@ -455,7 +455,7 @@ If you don't specify the `servers` parameter and `root_path` is equal to `/`, th
|
||||
|
||||
If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
|
||||
|
||||
and then it won't include it in the OpenAPI schema.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ This is because by default, FastAPI will inspect every item inside and make sure
|
||||
|
||||
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_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -55,7 +55,7 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||
* Import `HTMLResponse`.
|
||||
* Pass `HTMLResponse` as the parameter `response_class` of your *path operation decorator*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -73,7 +73,7 @@ As seen in [Return a Response directly](response-directly.md){.internal-link tar
|
||||
|
||||
The same example from above, returning an `HTMLResponse`, could look like:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *}
|
||||
|
||||
/// warning
|
||||
|
||||
@@ -97,7 +97,7 @@ The `response_class` will then be used only to document the OpenAPI *path operat
|
||||
|
||||
For example, it could be something like:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *}
|
||||
|
||||
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
|
||||
|
||||
@@ -136,7 +136,7 @@ It accepts the following parameters:
|
||||
|
||||
FastAPI (actually Starlette) will automatically include a Content-Length header. It will also include a Content-Type header, based on the `media_type` and appending a charset for text types.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
@@ -146,7 +146,7 @@ Takes some text or bytes and returns an HTML response, as you read above.
|
||||
|
||||
Takes some text or bytes and returns a plain text response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
@@ -180,7 +180,7 @@ This requires installing `ujson` for example with `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -194,14 +194,14 @@ Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default
|
||||
|
||||
You can return a `RedirectResponse` directly:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
Or you can use it in the `response_class` parameter:
|
||||
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *}
|
||||
|
||||
If you do that, then you can return the URL directly from your *path operation* function.
|
||||
|
||||
@@ -211,13 +211,13 @@ In this case, the `status_code` used will be the default one for the `RedirectRe
|
||||
|
||||
You can also use the `status_code` parameter combined with the `response_class` parameter:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Takes an async generator or a normal generator/iterator and streams the response body.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py310.py hl[2,14] *}
|
||||
|
||||
#### Using `StreamingResponse` with file-like objects { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
@@ -227,7 +227,7 @@ That way, you don't have to read it all first in memory, and you can pass that g
|
||||
|
||||
This includes many libraries to interact with cloud storage, video processing, and others.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py310.py hl[2,10:12,14] *}
|
||||
|
||||
1. This is the generator function. It's a "generator function" because it contains `yield` statements inside.
|
||||
2. By using a `with` block, we make sure that the file-like object is closed after the generator function is done. So, after it finishes sending the response.
|
||||
@@ -256,11 +256,11 @@ Takes a different set of arguments to instantiate than the other response types:
|
||||
|
||||
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *}
|
||||
|
||||
You can also use the `response_class` parameter:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *}
|
||||
|
||||
In this case, you can return the file path directly from your *path operation* function.
|
||||
|
||||
@@ -274,7 +274,7 @@ Let's say you want it to return indented and formatted JSON, so you want to use
|
||||
|
||||
You could create a `CustomORJSONResponse`. The main thing you have to do is create a `Response.render(content)` method that returns the content as `bytes`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *}
|
||||
|
||||
Now instead of returning:
|
||||
|
||||
@@ -300,7 +300,7 @@ 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`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ In that case, you can simply swap the standard `dataclasses` with `pydantic.data
|
||||
|
||||
6. Here we are returning a dictionary that contains `items` which is a list of dataclasses.
|
||||
|
||||
FastAPI is still capable of <abbr title="converting the data to a format that can be transmitted">serializing</abbr> the data to JSON.
|
||||
FastAPI is still capable of <dfn title="converting the data to a format that can be transmitted">serializing</dfn> the data to JSON.
|
||||
|
||||
7. Here the `response_model` is using a type annotation of a list of `Author` dataclasses.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Let's start with an example and then see it in detail.
|
||||
|
||||
We create an async function `lifespan()` with `yield` like this:
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
|
||||
|
||||
Here we are simulating the expensive *startup* operation of loading the model by putting the (fake) model function in the dictionary with machine learning models before the `yield`. This code will be executed **before** the application **starts taking requests**, during the *startup*.
|
||||
|
||||
@@ -48,7 +48,7 @@ Maybe you need to start a new version, or you just got tired of running it. 🤷
|
||||
|
||||
The first thing to notice, is that we are defining an async function with `yield`. This is very similar to Dependencies with `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
|
||||
|
||||
The first part of the function, before the `yield`, will be executed **before** the application starts.
|
||||
|
||||
@@ -60,7 +60,7 @@ If you check, the function is decorated with an `@asynccontextmanager`.
|
||||
|
||||
That converts the function into something called an "**async context manager**".
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
|
||||
|
||||
A **context manager** in Python is something that you can use in a `with` statement, for example, `open()` can be used as a context manager:
|
||||
|
||||
@@ -82,7 +82,7 @@ In our code example above, we don't use it directly, but we pass it to FastAPI f
|
||||
|
||||
The `lifespan` parameter of the `FastAPI` app takes an **async context manager**, so we can pass our new `lifespan` async context manager to it.
|
||||
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
|
||||
|
||||
## Alternative Events (deprecated) { #alternative-events-deprecated }
|
||||
|
||||
@@ -104,7 +104,7 @@ These functions can be declared with `async def` or normal `def`.
|
||||
|
||||
To add a function that should be run before the application starts, declare it with the event `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
|
||||
|
||||
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
|
||||
|
||||
@@ -116,7 +116,7 @@ And your application won't start receiving requests until all the `startup` even
|
||||
|
||||
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
|
||||
|
||||
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Some of these solutions may also be open source or offer free tiers, so you can
|
||||
|
||||
Let's start with a simple FastAPI application:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Notice that the *path operations* define the models they use for request payload and response payload, using the models `Item` and `ResponseMessage`.
|
||||
|
||||
@@ -98,7 +98,7 @@ In many cases, your FastAPI app will be bigger, and you will probably use tags t
|
||||
|
||||
For example, you could have a section for **items** and another section for **users**, and they could be separated by tags:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *}
|
||||
|
||||
### Generate a TypeScript Client with Tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
@@ -145,7 +145,7 @@ For example, here it is using the first tag (you will probably have only one tag
|
||||
|
||||
You can then pass that custom function to **FastAPI** as the `generate_unique_id_function` parameter:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *}
|
||||
|
||||
### Generate a TypeScript Client with Custom Operation IDs { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
@@ -167,7 +167,7 @@ But for the generated client, we could **modify** the OpenAPI operation IDs righ
|
||||
|
||||
We could download the OpenAPI JSON to a file `openapi.json` and then we could **remove that prefixed tag** with a script like this:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py310.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
|
||||
@@ -57,13 +57,13 @@ Enforces that all incoming requests must either be `https` or `wss`.
|
||||
|
||||
Any incoming request to `http` or `ws` will be redirected to the secure scheme instead.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *}
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
@@ -78,7 +78,7 @@ Handles GZip responses for any request that includes `"gzip"` in the `Accept-Enc
|
||||
|
||||
The middleware will handle both standard and streaming responses.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *}
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Webhooks are available in OpenAPI 3.1.0 and above, supported by FastAPI `0.99.0`
|
||||
|
||||
When you create a **FastAPI** application, there is a `webhooks` attribute that you can use to define *webhooks*, the same way you would define *path operations*, for example with `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:13,36:53] *}
|
||||
|
||||
The webhooks that you define will end up in the **OpenAPI** schema and the automatic **docs UI**.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ You can set the OpenAPI `operationId` to be used in your *path operation* with t
|
||||
|
||||
You would have to make sure that it is unique for each operation.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *}
|
||||
|
||||
### Using the *path operation function* name as the operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
@@ -20,7 +20,7 @@ If you want to use your APIs' function names as `operationId`s, you can iterate
|
||||
|
||||
You should do it after adding all your *path operations*.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -40,7 +40,7 @@ Even if they are in different modules (Python files).
|
||||
|
||||
To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *}
|
||||
|
||||
## Advanced description from docstring { #advanced-description-from-docstring }
|
||||
|
||||
@@ -92,7 +92,7 @@ You can extend the OpenAPI schema for a *path operation* using the parameter `op
|
||||
|
||||
This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *}
|
||||
|
||||
If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*.
|
||||
|
||||
@@ -139,9 +139,9 @@ For example, you could decide to read and validate the request with your own cod
|
||||
|
||||
You could do that with `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *}
|
||||
|
||||
In this example, we didn't declare any Pydantic model. In fact, the request body is not even <abbr title="converted from some plain format, like bytes, into Python objects">parsed</abbr> as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
|
||||
In this example, we didn't declare any Pydantic model. In fact, the request body is not even <dfn title="converted from some plain format, like bytes, into Python objects">parsed</dfn> as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way.
|
||||
|
||||
Nevertheless, we can declare the expected schema for the request body.
|
||||
|
||||
@@ -153,7 +153,7 @@ And you could do this even if the data type in the request is not JSON.
|
||||
|
||||
For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *}
|
||||
|
||||
Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML.
|
||||
|
||||
@@ -161,7 +161,7 @@ Then we use the request directly, and extract the body as `bytes`. This means th
|
||||
|
||||
And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set the `status_code` in that *temporal* response object.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *}
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set cookies in that *temporal* response object.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *}
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
||||
@@ -24,7 +24,7 @@ To do that, you can create a response as described in [Return a Response Directl
|
||||
|
||||
Then set Cookies in it, and then return it:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ Let's say that you want to return an <a href="https://en.wikipedia.org/wiki/XML"
|
||||
|
||||
You could put your XML content in a string, put that in a `Response`, and return it:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## Notes { #notes }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set headers in that *temporal* response object.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *}
|
||||
|
||||
And then you can return any object you need, as you normally would (a `dict`, a database model, etc).
|
||||
|
||||
@@ -22,7 +22,7 @@ You can also add headers when you return a `Response` directly.
|
||||
|
||||
Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *}
|
||||
|
||||
/// note | Technical Details
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Then, when you type that username and password, the browser sends them in the he
|
||||
* It returns an object of type `HTTPBasicCredentials`:
|
||||
* It contains the `username` and `password` sent.
|
||||
|
||||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||||
|
||||
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
|
||||
|
||||
@@ -40,7 +40,7 @@ To handle that, we first convert the `username` and `password` to `bytes` encodi
|
||||
|
||||
Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`.
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[1,12:24] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||||
|
||||
This would be similar to:
|
||||
|
||||
@@ -104,4 +104,4 @@ That way, using `secrets.compare_digest()` in your application code, it will be
|
||||
|
||||
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
|
||||
|
||||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|
||||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|
||||
|
||||
@@ -54,7 +54,7 @@ The same way as with Pydantic models, you declare class attributes with type ann
|
||||
|
||||
You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`.
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -70,7 +70,7 @@ Next it will convert and validate the data. So, when you use that `settings` obj
|
||||
|
||||
Then you can use the new `settings` object in your application:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *}
|
||||
|
||||
### Run the server { #run-the-server }
|
||||
|
||||
@@ -104,11 +104,11 @@ You could put those settings in another module file as you saw in [Bigger Applic
|
||||
|
||||
For example, you could have a file `config.py` with:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
{* ../../docs_src/settings/app01_py310/config.py *}
|
||||
|
||||
And then use it in a file `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -126,7 +126,7 @@ This could be especially useful during testing, as it's very easy to override a
|
||||
|
||||
Coming from the previous example, your `config.py` file could look like:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *}
|
||||
|
||||
Notice that now we don't create a default instance `settings = Settings()`.
|
||||
|
||||
@@ -134,7 +134,7 @@ Notice that now we don't create a default instance `settings = Settings()`.
|
||||
|
||||
Now we create a dependency that returns a new `config.Settings()`.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[6,12:13] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -146,13 +146,13 @@ For now you can assume `get_settings()` is a normal function.
|
||||
|
||||
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *}
|
||||
|
||||
### Settings and testing { #settings-and-testing }
|
||||
|
||||
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
In the dependency override we set a new value for the `admin_email` when creating the new `Settings` object, and then we return that new object.
|
||||
|
||||
@@ -193,7 +193,7 @@ APP_NAME="ChimichangApp"
|
||||
|
||||
And then update your `config.py` with:
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -226,7 +226,7 @@ we would create that object for each request, and we would be reading the `.env`
|
||||
|
||||
But as we are using the `@lru_cache` decorator on top, the `Settings` object will be created only once, the first time it's called. ✔️
|
||||
|
||||
{* ../../docs_src/settings/app03_an_py39/main.py hl[1,11] *}
|
||||
{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *}
|
||||
|
||||
Then for any subsequent call of `get_settings()` in the dependencies for the next requests, instead of executing the internal code of `get_settings()` and creating a new `Settings` object, it will return the same object that was returned on the first call, again and again.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ If you need to have two independent FastAPI applications, with their own indepen
|
||||
|
||||
First, create the main, top-level, **FastAPI** application, and its *path operations*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *}
|
||||
|
||||
### Sub-application { #sub-application }
|
||||
|
||||
@@ -18,7 +18,7 @@ Then, create your sub-application, and its *path operations*.
|
||||
|
||||
This sub-application is just another standard FastAPI application, but this is the one that will be "mounted":
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *}
|
||||
|
||||
### Mount the sub-application { #mount-the-sub-application }
|
||||
|
||||
@@ -26,7 +26,7 @@ In your top-level application, `app`, mount the sub-application, `subapi`.
|
||||
|
||||
In this case, it will be mounted at the path `/subapi`:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *}
|
||||
|
||||
### Check the automatic API docs { #check-the-automatic-api-docs }
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ $ pip install jinja2
|
||||
* Declare a `Request` parameter in the *path operation* that will return a template.
|
||||
* Use the `templates` you created to render and return a `TemplateResponse`, pass the name of the template, the request object, and a "context" dictionary with key-value pairs to be used inside of the Jinja2 template.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *}
|
||||
|
||||
/// note
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
When you need `lifespan` to run in your tests, you can use the `TestClient` with a `with` statement:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
You can read more details about the ["Running lifespan in tests in the official Starlette documentation site."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
|
||||
|
||||
For the deprecated `startup` and `shutdown` events, you can use the `TestClient` as follows:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *}
|
||||
|
||||
@@ -4,7 +4,7 @@ You can use the same `TestClient` to test WebSockets.
|
||||
|
||||
For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *}
|
||||
|
||||
/// note
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Let's imagine you want to get the client's IP address/host inside of your *path
|
||||
|
||||
For that you need to access the request directly.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *}
|
||||
|
||||
By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter.
|
||||
|
||||
|
||||
@@ -38,13 +38,13 @@ In production you would have one of the options above.
|
||||
|
||||
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Create a `websocket` { #create-a-websocket }
|
||||
|
||||
In your **FastAPI** application, create a `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[1,46:47] *}
|
||||
|
||||
/// note | Technical Details
|
||||
|
||||
@@ -58,7 +58,7 @@ You could also use `from starlette.websockets import WebSocket`.
|
||||
|
||||
In your WebSocket route you can `await` for messages and send messages.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py310.py hl[48:52] *}
|
||||
|
||||
You can receive and send binary, text, and JSON data.
|
||||
|
||||
@@ -154,7 +154,7 @@ With that you can connect the WebSocket and then send and receive messages:
|
||||
|
||||
When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial003_py39.py hl[79:81] *}
|
||||
{* ../../docs_src/websockets/tutorial003_py310.py hl[79:81] *}
|
||||
|
||||
To try it out:
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Then wrap the WSGI (e.g. Flask) app with the middleware.
|
||||
|
||||
And then mount that under a path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[1,3,23] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *}
|
||||
|
||||
/// note
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ There are several Flask REST frameworks, but after investing the time and work i
|
||||
|
||||
### <a href="https://marshmallow.readthedocs.io/en/stable/" class="external-link" target="_blank">Marshmallow</a> { #marshmallow }
|
||||
|
||||
One of the main features needed by API systems is data "<abbr title="also called marshalling, conversion">serialization</abbr>" which is taking data from the code (Python) and converting it into something that can be sent through the network. For example, converting an object containing data from a database into a JSON object. Converting `datetime` objects into strings, etc.
|
||||
One of the main features needed by API systems is data "<dfn title="also called marshalling, conversion">serialization</dfn>" which is taking data from the code (Python) and converting it into something that can be sent through the network. For example, converting an object containing data from a database into a JSON object. Converting `datetime` objects into strings, etc.
|
||||
|
||||
Another big feature needed by APIs is data validation, making sure that the data is valid, given certain parameters. For example, that some field is an `int`, and not some random string. This is especially useful for incoming data.
|
||||
|
||||
@@ -145,7 +145,7 @@ Without a data validation system, you would have to do all the checks by hand, i
|
||||
|
||||
These features are what Marshmallow was built to provide. It is a great library, and I have used it a lot before.
|
||||
|
||||
But it was created before there existed Python type hints. So, to define every <abbr title="the definition of how data should be formed">schema</abbr> you need to use specific utils and classes provided by Marshmallow.
|
||||
But it was created before there existed Python type hints. So, to define every <dfn title="the definition of how data should be formed">schema</dfn> you need to use specific utils and classes provided by Marshmallow.
|
||||
|
||||
/// check | Inspired **FastAPI** to
|
||||
|
||||
@@ -155,7 +155,7 @@ Use code to define "schemas" that provide data types and validation, automatical
|
||||
|
||||
### <a href="https://webargs.readthedocs.io/en/latest/" class="external-link" target="_blank">Webargs</a> { #webargs }
|
||||
|
||||
Another big feature required by APIs is <abbr title="reading and converting to Python data">parsing</abbr> data from incoming requests.
|
||||
Another big feature required by APIs is <dfn title="reading and converting to Python data">parsing</dfn> data from incoming requests.
|
||||
|
||||
Webargs is a tool that was made to provide that on top of several frameworks, including Flask.
|
||||
|
||||
@@ -419,7 +419,7 @@ Handle all the data validation, data serialization and automatic model documenta
|
||||
|
||||
### <a href="https://www.starlette.dev/" class="external-link" target="_blank">Starlette</a> { #starlette }
|
||||
|
||||
Starlette is a lightweight <abbr title="The new standard for building asynchronous Python web applications">ASGI</abbr> framework/toolkit, which is ideal for building high-performance asyncio services.
|
||||
Starlette is a lightweight <dfn title="The new standard for building asynchronous Python web applications">ASGI</dfn> framework/toolkit, which is ideal for building high-performance asyncio services.
|
||||
|
||||
It is very simple and intuitive. It's designed to be easily extensible, and have modular components.
|
||||
|
||||
|
||||
@@ -203,3 +203,8 @@ Inspired by Termynal's CSS tricks with modifications
|
||||
-webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
|
||||
box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
|
||||
}
|
||||
|
||||
.md-typeset dfn {
|
||||
border-bottom: .05rem dotted var(--md-default-fg-color--light);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ In a hurry and already know this stuff? Jump to the [`Dockerfile` below 👇](#b
|
||||
<summary>Dockerfile Preview 👀</summary>
|
||||
|
||||
```Dockerfile
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -166,7 +166,7 @@ Now in the same project directory create a file `Dockerfile` with:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)!
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
# (2)!
|
||||
WORKDIR /code
|
||||
@@ -390,7 +390,7 @@ If your FastAPI is a single file, for example, `main.py` without an `./app` dire
|
||||
Then you would just have to change the corresponding paths to copy the file inside the `Dockerfile`:
|
||||
|
||||
```{ .dockerfile .annotate hl_lines="10 13" }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
@@ -454,7 +454,7 @@ Without using containers, making applications run on startup and with restarts c
|
||||
|
||||
## Replication - Number of Processes { #replication-number-of-processes }
|
||||
|
||||
If you have a <abbr title="A group of machines that are configured to be connected and work together in some way.">cluster</abbr> of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Uvicorn with workers) in each container.
|
||||
If you have a <dfn title="A group of machines that are configured to be connected and work together in some way.">cluster</dfn> of machines with **Kubernetes**, Docker Swarm Mode, Nomad, or another similar complex system to manage distributed containers on multiple machines, then you will probably want to **handle replication** at the **cluster level** instead of using a **process manager** (like Uvicorn with workers) in each container.
|
||||
|
||||
One of those distributed container management systems like Kubernetes normally has some integrated way of handling **replication of containers** while still supporting **load balancing** for the incoming requests. All at the **cluster level**.
|
||||
|
||||
@@ -499,7 +499,7 @@ Of course, there are **special cases** where you could want to have **a containe
|
||||
In those cases, you can use the `--workers` command line option to set the number of workers that you want to run:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
FROM python:3.9
|
||||
FROM python:3.14
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ Here's an example of how an HTTPS API could look like, step by step, paying atte
|
||||
|
||||
It would probably all start by you **acquiring** some **domain name**. Then, you would configure it in a DNS server (possibly your same cloud provider).
|
||||
|
||||
You would probably get a cloud server (a virtual machine) or something similar, and it would have a <abbr title="That doesn't change">fixed</abbr> **public IP address**.
|
||||
You would probably get a cloud server (a virtual machine) or something similar, and it would have a <dfn title="Doesn't change over time. Not dynamic.">fixed</dfn> **public IP address**.
|
||||
|
||||
In the DNS server(s) you would configure a record (an "`A record`") to point **your domain** to the public **IP address of your server**.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
### Based on open standards { #based-on-open-standards }
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> for API creation, including declarations of <abbr title="also known as: endpoints, routes">path</abbr> <abbr title="also known as HTTP methods, as POST, GET, PUT, DELETE">operations</abbr>, parameters, request bodies, security, etc.
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification" class="external-link" target="_blank"><strong>OpenAPI</strong></a> for API creation, including declarations of <dfn title="also known as: endpoints, routes">path</dfn> <dfn title="also known as HTTP methods, as POST, GET, PUT, DELETE">operations</dfn>, parameters, request bodies, security, etc.
|
||||
* Automatic data model documentation with <a href="https://json-schema.org/" class="external-link" target="_blank"><strong>JSON Schema</strong></a> (as OpenAPI itself is based on JSON Schema).
|
||||
* Designed around these standards, after a meticulous study. Instead of an afterthought layer on top.
|
||||
* This also allows using automatic **client code generation** in many languages.
|
||||
@@ -136,7 +136,7 @@ All built as reusable tools and components that are easy to integrate with your
|
||||
|
||||
### Dependency Injection { #dependency-injection }
|
||||
|
||||
FastAPI includes an extremely easy to use, but extremely powerful <abbr title='also known as "components", "resources", "services", "providers"'><strong>Dependency Injection</strong></abbr> system.
|
||||
FastAPI includes an extremely easy to use, but extremely powerful <dfn title='also known as "components", "resources", "services", "providers"'><strong>Dependency Injection</strong></dfn> system.
|
||||
|
||||
* Even dependencies can have dependencies, creating a hierarchy or **"graph" of dependencies**.
|
||||
* All **automatically handled** by the framework.
|
||||
@@ -153,8 +153,8 @@ Any integration is designed to be so simple to use (with dependencies) that you
|
||||
|
||||
### Tested { #tested }
|
||||
|
||||
* 100% <abbr title="The amount of code that is automatically tested">test coverage</abbr>.
|
||||
* 100% <abbr title="Python type annotations, with this your editor and external tools can give you better support">type annotated</abbr> code base.
|
||||
* 100% <dfn title="The amount of code that is automatically tested">test coverage</dfn>.
|
||||
* 100% <dfn title="Python type annotations, with this your editor and external tools can give you better support">type annotated</dfn> code base.
|
||||
* Used in production applications.
|
||||
|
||||
## Starlette features { #starlette-features }
|
||||
@@ -190,7 +190,7 @@ With **FastAPI** you get all of **Pydantic**'s features (as FastAPI is based on
|
||||
* **No brainfuck**:
|
||||
* No new schema definition micro-language to learn.
|
||||
* If you know Python types you know how to use Pydantic.
|
||||
* Plays nicely with your **<abbr title="Integrated Development Environment: similar to a code editor">IDE</abbr>/<abbr title="A program that checks for code errors">linter</abbr>/brain**:
|
||||
* Plays nicely with your **<abbr title="Integrated Development Environment: similar to a code editor">IDE</abbr>/<dfn title="A program that checks for code errors">linter</dfn>/brain**:
|
||||
* Because pydantic data structures are just instances of classes you define; auto-completion, linting, mypy and your intuition should all work properly with your validated data.
|
||||
* Validate **complex structures**:
|
||||
* Use of hierarchical Pydantic models, Python `typing`’s `List` and `Dict`, etc.
|
||||
|
||||
@@ -8,7 +8,7 @@ But if for some reason your clients depend on the old behavior, you can revert t
|
||||
|
||||
For example, you can create a subclass of `HTTPBearer` that returns a `403 Forbidden` error instead of the default `401 Unauthorized` error:
|
||||
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py39.py hl[9:13] *}
|
||||
{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ You can easily use the same Pydantic settings to configure your generated OpenAP
|
||||
|
||||
For example:
|
||||
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *}
|
||||
|
||||
Here we declare the setting `openapi_url` with the same default of `"/openapi.json"`.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Without changing the settings, syntax highlighting is enabled by default:
|
||||
|
||||
But you can disable it by setting `syntaxHighlight` to `False`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *}
|
||||
|
||||
...and then Swagger UI won't show the syntax highlighting anymore:
|
||||
|
||||
@@ -28,7 +28,7 @@ But you can disable it by setting `syntaxHighlight` to `False`:
|
||||
|
||||
The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle):
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *}
|
||||
|
||||
That configuration would change the syntax highlighting color theme:
|
||||
|
||||
@@ -46,7 +46,7 @@ You can override any of them by setting a different value in the argument `swagg
|
||||
|
||||
For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`:
|
||||
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *}
|
||||
|
||||
## Other Swagger UI Parameters { #other-swagger-ui-parameters }
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ The first step is to disable the automatic docs, as by default, those use the de
|
||||
|
||||
To disable them, set their URLs to `None` when creating your `FastAPI` app:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *}
|
||||
|
||||
### Include the custom docs { #include-the-custom-docs }
|
||||
|
||||
@@ -34,7 +34,7 @@ You can reuse FastAPI's internal functions to create the HTML pages for the docs
|
||||
|
||||
And similarly for ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[2:6,11:19,22:24,27:33] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -50,7 +50,7 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
|
||||
|
||||
Now, to be able to test that everything works, create a *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py39.py hl[36:38] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *}
|
||||
|
||||
### Test it { #test-it }
|
||||
|
||||
@@ -118,7 +118,7 @@ After that, your file structure could look like:
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[7,11] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *}
|
||||
|
||||
### Test the static files { #test-the-static-files }
|
||||
|
||||
@@ -144,7 +144,7 @@ The same as when using a custom CDN, the first step is to disable the automatic
|
||||
|
||||
To disable them, set their URLs to `None` when creating your `FastAPI` app:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[9] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *}
|
||||
|
||||
### Include the custom docs for static files { #include-the-custom-docs-for-static-files }
|
||||
|
||||
@@ -160,7 +160,7 @@ Again, you can reuse FastAPI's internal functions to create the HTML pages for t
|
||||
|
||||
And similarly for ReDoc...
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[2:6,14:22,25:27,30:36] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -176,7 +176,7 @@ Swagger UI will handle it behind the scenes for you, but it needs this "redirect
|
||||
|
||||
Now, to be able to test that everything works, create a *path operation*:
|
||||
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py39.py hl[39:41] *}
|
||||
{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *}
|
||||
|
||||
### Test Static Files UI { #test-static-files-ui }
|
||||
|
||||
|
||||
@@ -43,19 +43,19 @@ For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/doc
|
||||
|
||||
First, write all your **FastAPI** application as normally:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[1,4,7:9] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *}
|
||||
|
||||
### Generate the OpenAPI schema { #generate-the-openapi-schema }
|
||||
|
||||
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[2,15:21] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *}
|
||||
|
||||
### Modify the OpenAPI schema { #modify-the-openapi-schema }
|
||||
|
||||
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[22:24] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *}
|
||||
|
||||
### Cache the OpenAPI schema { #cache-the-openapi-schema }
|
||||
|
||||
@@ -65,13 +65,13 @@ That way, your application won't have to generate the schema every time a user o
|
||||
|
||||
It will be generated only once, and then the same cached schema will be used for the next requests.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[13:14,25:26] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *}
|
||||
|
||||
### Override the method { #override-the-method }
|
||||
|
||||
Now you can replace the `.openapi()` method with your new function.
|
||||
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py39.py hl[29] *}
|
||||
{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *}
|
||||
|
||||
### Check it { #check-it }
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Depending on your use case, you might prefer to use a different library, but if
|
||||
|
||||
Here's a small preview of how you could integrate Strawberry with FastAPI:
|
||||
|
||||
{* ../../docs_src/graphql_/tutorial001_py39.py hl[3,22,25] *}
|
||||
{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *}
|
||||
|
||||
You can learn more about Strawberry in the <a href="https://strawberry.rocks/" class="external-link" target="_blank">Strawberry documentation</a>.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ The key features are:
|
||||
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
|
||||
* **Fast to code**: Increase the speed to develop features by about 200% to 300%. *
|
||||
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
|
||||
* **Intuitive**: Great editor support. <dfn title="also known as auto-complete, autocompletion, IntelliSense">Completion</dfn> everywhere. Less time debugging.
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
@@ -368,7 +368,7 @@ item: Item
|
||||
* Validation of data:
|
||||
* Automatic and clear errors when the data is invalid.
|
||||
* Validation even for deeply nested JSON objects.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of input data: coming from the network to Python data and types. Reading from:
|
||||
* <dfn title="also known as: serialization, parsing, marshalling">Conversion</dfn> of input data: coming from the network to Python data and types. Reading from:
|
||||
* JSON.
|
||||
* Path parameters.
|
||||
* Query parameters.
|
||||
@@ -376,7 +376,7 @@ item: Item
|
||||
* Headers.
|
||||
* Forms.
|
||||
* Files.
|
||||
* <abbr title="also known as: serialization, parsing, marshalling">Conversion</abbr> of output data: converting from Python data and types to network data (as JSON):
|
||||
* <dfn title="also known as: serialization, parsing, marshalling">Conversion</dfn> of output data: converting from Python data and types to network data (as JSON):
|
||||
* Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc).
|
||||
* `datetime` objects.
|
||||
* `UUID` objects.
|
||||
@@ -439,7 +439,7 @@ For a more complete example including more features, see the <a href="https://fa
|
||||
|
||||
* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**.
|
||||
* How to set **validation constraints** as `maximum_length` or `regex`.
|
||||
* A very powerful and easy to use **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
* A very powerful and easy to use **<dfn title="also known as components, resources, providers, services, injectables">Dependency Injection</dfn>** system.
|
||||
* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth.
|
||||
* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic).
|
||||
* **GraphQL** integration with <a href="https://strawberry.rocks" class="external-link" target="_blank">Strawberry</a> and other libraries.
|
||||
@@ -524,7 +524,7 @@ Used by Starlette:
|
||||
|
||||
* <a href="https://www.python-httpx.org" target="_blank"><code>httpx</code></a> - Required if you want to use the `TestClient`.
|
||||
* <a href="https://jinja.palletsprojects.com" target="_blank"><code>jinja2</code></a> - Required if you want to use the default template configuration.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>, with `request.form()`.
|
||||
* <a href="https://github.com/Kludex/python-multipart" target="_blank"><code>python-multipart</code></a> - Required if you want to support form <dfn title="converting the string that comes from an HTTP request into Python data">"parsing"</dfn>, with `request.form()`.
|
||||
|
||||
Used by FastAPI:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Python has support for optional "type hints" (also called "type annotations").
|
||||
|
||||
These **"type hints"** or annotations are a special syntax that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
|
||||
These **"type hints"** or annotations are a special syntax that allow declaring the <dfn title="for example: str, int, float, bool">type</dfn> of a variable.
|
||||
|
||||
By declaring types for your variables, editors and tools can give you better support.
|
||||
|
||||
@@ -22,7 +22,7 @@ If you are a Python expert, and you already know everything about type hints, sk
|
||||
|
||||
Let's start with a simple example:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py *}
|
||||
|
||||
Calling this program outputs:
|
||||
|
||||
@@ -34,9 +34,9 @@ The function does the following:
|
||||
|
||||
* Takes a `first_name` and `last_name`.
|
||||
* Converts the first letter of each one to upper case with `title()`.
|
||||
* <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
|
||||
* <dfn title="Puts them together, as one. With the contents of one after the other.">Concatenates</dfn> them with a space in the middle.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial001_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial001_py310.py hl[2] *}
|
||||
|
||||
### Edit it { #edit-it }
|
||||
|
||||
@@ -78,7 +78,7 @@ That's it.
|
||||
|
||||
Those are the "type hints":
|
||||
|
||||
{* ../../docs_src/python_types/tutorial002_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial002_py310.py hl[1] *}
|
||||
|
||||
That is not the same as declaring default values like would be with:
|
||||
|
||||
@@ -106,7 +106,7 @@ With that, you can scroll, seeing the options, until you find the one that "ring
|
||||
|
||||
Check this function, it already has type hints:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial003_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial003_py310.py hl[1] *}
|
||||
|
||||
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
|
||||
|
||||
@@ -114,7 +114,7 @@ Because the editor knows the types of the variables, you don't only get completi
|
||||
|
||||
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial004_py39.py hl[2] *}
|
||||
{* ../../docs_src/python_types/tutorial004_py310.py hl[2] *}
|
||||
|
||||
## Declaring types { #declaring-types }
|
||||
|
||||
@@ -133,29 +133,32 @@ You can use, for example:
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
{* ../../docs_src/python_types/tutorial005_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial005_py310.py hl[1] *}
|
||||
|
||||
### Generic types with type parameters { #generic-types-with-type-parameters }
|
||||
### `typing` module { #typing-module }
|
||||
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
For some additional use cases, you might need to import some things from the standard library `typing` module, for example when you want to declare that something has "any type", you can use `Any` from `typing`:
|
||||
|
||||
These types that have internal types are called "**generic**" types. And it's possible to declare them, even with their internal types.
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
To declare those types and the internal types, you can use the standard Python module `typing`. It exists specifically to support these type hints.
|
||||
|
||||
#### Newer versions of Python { #newer-versions-of-python }
|
||||
def some_function(data: Any):
|
||||
print(data)
|
||||
```
|
||||
|
||||
The syntax using `typing` is **compatible** with all versions, from Python 3.6 to the latest ones, including Python 3.9, Python 3.10, etc.
|
||||
### Generic types { #generic-types }
|
||||
|
||||
As Python advances, **newer versions** come with improved support for these type annotations and in many cases you won't even need to import and use the `typing` module to declare the type annotations.
|
||||
Some types can take "type parameters" in square brackets, to define their internal types, for example a "list of strings" would be declared `list[str]`.
|
||||
|
||||
If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra simplicity.
|
||||
These types that can take type parameters are called **Generic types** or **Generics**.
|
||||
|
||||
In all the docs there are examples compatible with each version of Python (when there's a difference).
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
|
||||
For example "**Python 3.6+**" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And "**Python 3.9+**" means it's compatible with Python 3.9 or above (including 3.10, etc).
|
||||
|
||||
If you can use the **latest versions of Python**, use the examples for the latest version, those will have the **best and simplest syntax**, for example, "**Python 3.10+**".
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
#### List { #list }
|
||||
|
||||
@@ -167,7 +170,7 @@ As the type, put `list`.
|
||||
|
||||
As the list is a type that contains some internal types, you put them in square brackets:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial006_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial006_py310.py hl[1] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -193,7 +196,7 @@ And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial007_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial007_py310.py hl[1] *}
|
||||
|
||||
This means:
|
||||
|
||||
@@ -208,7 +211,7 @@ The first type parameter is for the keys of the `dict`.
|
||||
|
||||
The second type parameter is for the values of the `dict`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial008_py39.py hl[1] *}
|
||||
{* ../../docs_src/python_types/tutorial008_py310.py hl[1] *}
|
||||
|
||||
This means:
|
||||
|
||||
@@ -220,44 +223,20 @@ This means:
|
||||
|
||||
You can declare that a variable can be any of **several types**, for example, an `int` or a `str`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can use the `Union` type from `typing` and put inside the square brackets the possible types to accept.
|
||||
To define it you use the <dfn title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</dfn> to separate both types.
|
||||
|
||||
In Python 3.10 there's also a **new syntax** where you can put the possible types separated by a <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr>.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
This is called a "union", because the variable can be anything in the union of those two sets of types.
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial008b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
In both cases this means that `item` could be an `int` or a `str`.
|
||||
This means that `item` could be an `int` or a `str`.
|
||||
|
||||
#### Possibly `None` { #possibly-none }
|
||||
|
||||
You can declare that a value could have a type, like `str`, but that it could also be `None`.
|
||||
|
||||
In Python 3.6 and above (including Python 3.10) you can declare it by importing and using `Optional` from the `typing` module.
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
Using `Optional[str]` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
`Optional[Something]` is actually a shortcut for `Union[Something, None]`, they are equivalent.
|
||||
|
||||
This also means that in Python 3.10, you can use `Something | None`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
@@ -266,96 +245,7 @@ This also means that in Python 3.10, you can use `Something | None`:
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ alternative
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../docs_src/python_types/tutorial009b_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Using `Union` or `Optional` { #using-union-or-optional }
|
||||
|
||||
If you are using a Python version below 3.10, here's a tip from my very **subjective** point of view:
|
||||
|
||||
* 🚨 Avoid using `Optional[SomeType]`
|
||||
* Instead ✨ **use `Union[SomeType, None]`** ✨.
|
||||
|
||||
Both are equivalent and underneath they are the same, but I would recommend `Union` instead of `Optional` because the word "**optional**" would seem to imply that the value is optional, and it actually means "it can be `None`", even if it's not optional and is still required.
|
||||
|
||||
I think `Union[SomeType, None]` is more explicit about what it means.
|
||||
|
||||
It's just about the words and names. But those words can affect how you and your teammates think about the code.
|
||||
|
||||
As an example, let's take this function:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py39.py hl[1,4] *}
|
||||
|
||||
The parameter `name` is defined as `Optional[str]`, but it is **not optional**, you cannot call the function without the parameter:
|
||||
|
||||
```Python
|
||||
say_hi() # Oh, no, this throws an error! 😱
|
||||
```
|
||||
|
||||
The `name` parameter is **still required** (not *optional*) because it doesn't have a default value. Still, `name` accepts `None` as the value:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # This works, None is valid 🎉
|
||||
```
|
||||
|
||||
The good news is, once you are on Python 3.10 you won't have to worry about that, as you will be able to simply use `|` to define unions of types:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial009c_py310.py hl[1,4] *}
|
||||
|
||||
And then you won't have to worry about names like `Optional` and `Union`. 😎
|
||||
|
||||
#### Generic types { #generic-types }
|
||||
|
||||
These types that take type parameters in square brackets are called **Generic types** or **Generics**, for example:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And the same as with previous Python versions, from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
In Python 3.10, as an alternative to using the generics `Union` and `Optional`, you can use the <abbr title='also called "bitwise or operator", but that meaning is not relevant here'>vertical bar (`|`)</abbr> to declare unions of types, that's a lot better and simpler.
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
You can use the same builtin types as generics (with square brackets and types inside):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
And generics from the `typing` module:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...and others.
|
||||
|
||||
////
|
||||
Using `str | None` instead of just `str` will let the editor help you detect errors where you could be assuming that a value is always a `str`, when it could actually be `None` too.
|
||||
|
||||
### Classes as types { #classes-as-types }
|
||||
|
||||
@@ -363,11 +253,11 @@ You can also declare a class as the type of a variable.
|
||||
|
||||
Let's say you have a class `Person`, with a name:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[1:3] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[1:3] *}
|
||||
|
||||
Then you can declare a variable to be of type `Person`:
|
||||
|
||||
{* ../../docs_src/python_types/tutorial010_py39.py hl[6] *}
|
||||
{* ../../docs_src/python_types/tutorial010_py310.py hl[6] *}
|
||||
|
||||
And then, again, you get all the editor support:
|
||||
|
||||
@@ -403,19 +293,13 @@ To learn more about <a href="https://docs.pydantic.dev/" class="external-link" t
|
||||
|
||||
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
|
||||
|
||||
/// tip
|
||||
|
||||
Pydantic has a special behavior when you use `Optional` or `Union[Something, None]` without a default value, you can read more about it in the Pydantic docs about <a href="https://docs.pydantic.dev/2.3/usage/models/#required-fields" class="external-link" target="_blank">Required Optional fields</a>.
|
||||
|
||||
///
|
||||
|
||||
## Type Hints with Metadata Annotations { #type-hints-with-metadata-annotations }
|
||||
|
||||
Python also has a feature that allows putting **additional <abbr title="Data about the data, in this case, information about the type, e.g. a description.">metadata</abbr>** in these type hints using `Annotated`.
|
||||
Python also has a feature that allows putting **additional <dfn title="Data about the data, in this case, information about the type, e.g. a description.">metadata</dfn>** in these type hints using `Annotated`.
|
||||
|
||||
Since Python 3.9, `Annotated` is a part of the standard library, so you can import it from `typing`.
|
||||
You can import `Annotated` from `typing`.
|
||||
|
||||
{* ../../docs_src/python_types/tutorial013_py39.py hl[1,4] *}
|
||||
{* ../../docs_src/python_types/tutorial013_py310.py hl[1,4] *}
|
||||
|
||||
Python itself doesn't do anything with this `Annotated`. And for editors and other tools, the type is still `str`.
|
||||
|
||||
|
||||
@@ -7,6 +7,61 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* ➖ Drop support for Python 3.9. PR [#14897](https://github.com/fastapi/fastapi/pull/14897) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Refactors
|
||||
|
||||
* 🎨 Update internal types for Python 3.10. PR [#14898](https://github.com/fastapi/fastapi/pull/14898) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.128.8
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Fix grammar in `docs/en/docs/tutorial/first-steps.md`. PR [#14708](https://github.com/fastapi/fastapi/pull/14708) by [@SanjanaS10](https://github.com/SanjanaS10).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔨 Tweak PDM hook script. PR [#14895](https://github.com/fastapi/fastapi/pull/14895) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Update build setup for `fastapi-slim`, deprecate it, and make it only depend on `fastapi`. PR [#14894](https://github.com/fastapi/fastapi/pull/14894) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.128.7
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Show a clear error on attempt to include router into itself. PR [#14258](https://github.com/fastapi/fastapi/pull/14258) by [@JavierSanchezCastro](https://github.com/JavierSanchezCastro).
|
||||
* ✨ Replace `dict` by `Mapping` on `HTTPException.headers`. PR [#12997](https://github.com/fastapi/fastapi/pull/12997) by [@rijenkii](https://github.com/rijenkii).
|
||||
|
||||
### Refactors
|
||||
|
||||
* ♻️ Simplify reading files in memory, do it sequentially instead of (fake) parallel. PR [#14884](https://github.com/fastapi/fastapi/pull/14884) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Use `dfn` tag for definitions instead of `abbr` in docs. PR [#14744](https://github.com/fastapi/fastapi/pull/14744) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Tweak comment in test to reference PR. PR [#14885](https://github.com/fastapi/fastapi/pull/14885) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update LLM-prompt for `abbr` and `dfn` tags. PR [#14747](https://github.com/fastapi/fastapi/pull/14747) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* ✅ Test order for the submitted byte Files. PR [#14828](https://github.com/fastapi/fastapi/pull/14828) by [@valentinDruzhinin](https://github.com/valentinDruzhinin).
|
||||
* 🔧 Configure `test` workflow to run tests with `inline-snapshot=review`. PR [#14876](https://github.com/fastapi/fastapi/pull/14876) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
## 0.128.6
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix `on_startup` and `on_shutdown` parameters of `APIRouter`. PR [#14873](https://github.com/fastapi/fastapi/pull/14873) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Update translations for zh (update-outdated). PR [#14843](https://github.com/fastapi/fastapi/pull/14843) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Internal
|
||||
|
||||
* ✅ Fix parameterized tests with snapshots. PR [#14875](https://github.com/fastapi/fastapi/pull/14875) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
## 0.128.5
|
||||
|
||||
### Refactors
|
||||
|
||||
@@ -15,7 +15,7 @@ This includes, for example:
|
||||
|
||||
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[1,13] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *}
|
||||
|
||||
**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter.
|
||||
|
||||
@@ -31,13 +31,13 @@ In this case, the task function will write to a file (simulating sending an emai
|
||||
|
||||
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[6:9] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *}
|
||||
|
||||
## Add the background task { #add-the-background-task }
|
||||
|
||||
Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`:
|
||||
|
||||
{* ../../docs_src/background_tasks/tutorial001_py39.py hl[14] *}
|
||||
{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *}
|
||||
|
||||
`.add_task()` receives as arguments:
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ You can create the *path operations* for that module using `APIRouter`.
|
||||
|
||||
You import it and create an "instance" the same way you would with the class `FastAPI`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *}
|
||||
|
||||
### *Path operations* with `APIRouter` { #path-operations-with-apirouter }
|
||||
|
||||
@@ -93,7 +93,7 @@ And then you use it to declare your *path operations*.
|
||||
|
||||
Use it the same way you would use the `FastAPI` class:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}
|
||||
|
||||
You can think of `APIRouter` as a "mini `FastAPI`" class.
|
||||
|
||||
@@ -117,7 +117,7 @@ So we put them in their own `dependencies` module (`app/dependencies.py`).
|
||||
|
||||
We will now use a simple dependency to read a custom `X-Token` header:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -149,7 +149,7 @@ We know all the *path operations* in this module have the same:
|
||||
|
||||
So, instead of adding all that to each *path operation*, we can add it to the `APIRouter`.
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}
|
||||
|
||||
As the path of each *path operation* has to start with `/`, like in:
|
||||
|
||||
@@ -208,7 +208,7 @@ And we need to get the dependency function from the module `app.dependencies`, t
|
||||
|
||||
So we use a relative import with `..` for the dependencies:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *}
|
||||
|
||||
#### How relative imports work { #how-relative-imports-work }
|
||||
|
||||
@@ -279,7 +279,7 @@ We are not adding the prefix `/items` nor the `tags=["items"]` to each *path ope
|
||||
|
||||
But we can still add _more_ `tags` that will be applied to a specific *path operation*, and also some extra `responses` specific to that *path operation*:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -305,13 +305,13 @@ You import and create a `FastAPI` class as normally.
|
||||
|
||||
And we can even declare [global dependencies](dependencies/global-dependencies.md){.internal-link target=_blank} that will be combined with the dependencies for each `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *}
|
||||
|
||||
### Import the `APIRouter` { #import-the-apirouter }
|
||||
|
||||
Now we import the other submodules that have `APIRouter`s:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *}
|
||||
|
||||
As the files `app/routers/users.py` and `app/routers/items.py` are submodules that are part of the same Python package `app`, we can use a single dot `.` to import them using "relative imports".
|
||||
|
||||
@@ -374,13 +374,13 @@ the `router` from `users` would overwrite the one from `items` and we wouldn't b
|
||||
|
||||
So, to be able to use both of them in the same file, we import the submodules directly:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *}
|
||||
|
||||
### Include the `APIRouter`s for `users` and `items` { #include-the-apirouters-for-users-and-items }
|
||||
|
||||
Now, let's include the `router`s from the submodules `users` and `items`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -420,13 +420,13 @@ It contains an `APIRouter` with some admin *path operations* that your organizat
|
||||
|
||||
For this example it will be super simple. But let's say that because it is shared with other projects in the organization, we cannot modify it and add a `prefix`, `dependencies`, `tags`, etc. directly to the `APIRouter`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *}
|
||||
|
||||
But we still want to set a custom `prefix` when including the `APIRouter` so that all its *path operations* start with `/admin`, we want to secure it with the `dependencies` we already have for this project, and we want to include `tags` and `responses`.
|
||||
|
||||
We can declare all that without having to modify the original `APIRouter` by passing those parameters to `app.include_router()`:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *}
|
||||
|
||||
That way, the original `APIRouter` will stay unmodified, so we can still share that same `app/internal/admin.py` file with other projects in the organization.
|
||||
|
||||
@@ -447,7 +447,7 @@ We can also add *path operations* directly to the `FastAPI` app.
|
||||
|
||||
Here we do it... just to show that we can 🤷:
|
||||
|
||||
{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}
|
||||
{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *}
|
||||
|
||||
and it will work correctly, together with all the other *path operations* added with `app.include_router()`.
|
||||
|
||||
|
||||
@@ -106,13 +106,6 @@ As, by default, singular values are interpreted as query parameters, you don't h
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
Or in Python 3.9:
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *}
|
||||
|
||||
@@ -164,7 +164,7 @@ images: list[Image]
|
||||
|
||||
as in:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py39.py hl[13] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *}
|
||||
|
||||
## Editor support everywhere { #editor-support-everywhere }
|
||||
|
||||
@@ -194,7 +194,7 @@ That's what we are going to see here.
|
||||
|
||||
In this case, you would accept any `dict` as long as it has `int` keys with `float` values:
|
||||
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py39.py hl[7] *}
|
||||
{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ The function parameters will be recognized as follows:
|
||||
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
||||
The `str | None` (Python 3.10+) or `Union` in `Union[str, None]` (Python 3.9+) is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
|
||||
The `str | None` is not used by FastAPI to determine that the value is not required, it will know it's not required because it has a default value of `= None`.
|
||||
|
||||
But adding the type annotations will allow your editor to give you better support and detect errors.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ But even if you **fill the data** and click "Execute", because the docs UI works
|
||||
|
||||
In some special use cases (probably not very common), you might want to **restrict** the cookies that you want to receive.
|
||||
|
||||
Your API now has the power to control its own <abbr title="This is a joke, just in case. It has nothing to do with cookie consents, but it's funny that even the API can now reject the poor cookies. Have a cookie. 🍪">cookie consent</abbr>. 🤪🍪
|
||||
Your API now has the power to control its own <dfn title="This is a joke, just in case. It has nothing to do with cookie consents, but it's funny that even the API can now reject the poor cookies. Have a cookie. 🍪">cookie consent</dfn>. 🤪🍪
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
@@ -54,9 +54,9 @@ You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
If a client tries to send some **extra cookies**, they will receive an **error** response.
|
||||
|
||||
Poor cookie banners with all their effort to get your consent for the <abbr title="This is another joke. Don't pay attention to me. Have some coffee for your cookie. ☕">API to reject it</abbr>. 🍪
|
||||
Poor cookie banners with all their effort to get your consent for the <dfn title="This is another joke. Don't pay attention to me. Have some coffee for your cookie. ☕">API to reject it</dfn>. 🍪
|
||||
|
||||
For example, if the client tries to send a `santa_tracker` cookie with a value of `good-list-please`, the client will receive an **error** response telling them that the `santa_tracker` <abbr title="Santa disapproves the lack of cookies. 🎅 Okay, no more cookie jokes.">cookie is not allowed</abbr>:
|
||||
For example, if the client tries to send a `santa_tracker` cookie with a value of `good-list-please`, the client will receive an **error** response telling them that the `santa_tracker` <dfn title="Santa disapproves the lack of cookies. 🎅 Okay, no more cookie jokes.">cookie is not allowed</dfn>:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -73,4 +73,4 @@ For example, if the client tries to send a `santa_tracker` cookie with a value o
|
||||
|
||||
## Summary { #summary }
|
||||
|
||||
You can use **Pydantic models** to declare <abbr title="Have a last cookie before you go. 🍪">**cookies**</abbr> in **FastAPI**. 😎
|
||||
You can use **Pydantic models** to declare <dfn title="Have a last cookie before you go. 🍪">**cookies**</dfn> in **FastAPI**. 😎
|
||||
|
||||
@@ -46,7 +46,7 @@ You can also specify whether your backend allows:
|
||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
||||
|
||||
{* ../../docs_src/cors/tutorial001_py39.py hl[2,6:11,13:19] *}
|
||||
{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *}
|
||||
|
||||
|
||||
The default parameters used by the `CORSMiddleware` implementation are restrictive by default, so you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be permitted to use them in a Cross-Domain context.
|
||||
|
||||
@@ -6,7 +6,7 @@ You can connect the debugger in your editor, for example with Visual Studio Code
|
||||
|
||||
In your FastAPI application, import and run `uvicorn` directly:
|
||||
|
||||
{* ../../docs_src/debugging/tutorial001_py39.py hl[1,15] *}
|
||||
{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *}
|
||||
|
||||
### About `__name__ == "__main__"` { #about-name-main }
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ Now you can declare your dependency using this class.
|
||||
|
||||
Notice how we write `CommonQueryParams` twice in the above code:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -109,7 +109,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -137,7 +137,7 @@ It is from this one that FastAPI will extract the declared parameters and that i
|
||||
|
||||
In this case, the first `CommonQueryParams`, in:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, ...
|
||||
@@ -145,7 +145,7 @@ commons: Annotated[CommonQueryParams, ...
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -163,7 +163,7 @@ commons: CommonQueryParams ...
|
||||
|
||||
You could actually write just:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
@@ -171,7 +171,7 @@ commons: Annotated[Any, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -197,7 +197,7 @@ But declaring the type is encouraged as that way your editor will know what will
|
||||
|
||||
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -205,7 +205,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -225,7 +225,7 @@ For those specific cases, you can do the following:
|
||||
|
||||
Instead of writing:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
@@ -233,7 +233,7 @@ commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -249,7 +249,7 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
|
||||
...you write:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
commons: Annotated[CommonQueryParams, Depends()]
|
||||
@@ -257,7 +257,7 @@ commons: Annotated[CommonQueryParams, Depends()]
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ The *path operation decorator* receives an optional argument `dependencies`.
|
||||
|
||||
It should be a `list` of `Depends()`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[19] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *}
|
||||
|
||||
These dependencies will be executed/solved the same way as normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
|
||||
|
||||
@@ -44,13 +44,13 @@ You can use the same dependency *functions* you use normally.
|
||||
|
||||
They can declare request requirements (like headers) or other sub-dependencies:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[8,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *}
|
||||
|
||||
### Raise exceptions { #raise-exceptions }
|
||||
|
||||
These dependencies can `raise` exceptions, the same as normal dependencies:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *}
|
||||
|
||||
### Return values { #return-values }
|
||||
|
||||
@@ -58,7 +58,7 @@ And they can return values or not, the values won't be used.
|
||||
|
||||
So, you can reuse a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py39.py hl[11,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *}
|
||||
|
||||
## Dependencies for a group of *path operations* { #dependencies-for-a-group-of-path-operations }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Dependencies with yield { #dependencies-with-yield }
|
||||
|
||||
FastAPI supports dependencies that do some <abbr title='sometimes also called "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code", etc.'>extra steps after finishing</abbr>.
|
||||
FastAPI supports dependencies that do some <dfn title='sometimes also called "exit code", "cleanup code", "teardown code", "closing code", "context manager exit code", etc.'>extra steps after finishing</dfn>.
|
||||
|
||||
To do this, use `yield` instead of `return`, and write the extra steps (code) after.
|
||||
|
||||
@@ -29,15 +29,15 @@ For example, you could use this to create a database session and close it after
|
||||
|
||||
Only the code prior to and including the `yield` statement is executed before creating a response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[2:4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *}
|
||||
|
||||
The yielded value is what is injected into *path operations* and other dependencies:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[4] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *}
|
||||
|
||||
The code following the `yield` statement is executed after the response:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[5:6] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -57,7 +57,7 @@ So, you can look for that specific exception inside the dependency with `except
|
||||
|
||||
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial007_py39.py hl[3,5] *}
|
||||
{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *}
|
||||
|
||||
## Sub-dependencies with `yield` { #sub-dependencies-with-yield }
|
||||
|
||||
@@ -67,7 +67,7 @@ You can have sub-dependencies and "trees" of sub-dependencies of any size and sh
|
||||
|
||||
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[6,14,22] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *}
|
||||
|
||||
And all of them can use `yield`.
|
||||
|
||||
@@ -75,7 +75,7 @@ In this case `dependency_c`, to execute its exit code, needs the value from `dep
|
||||
|
||||
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py39.py hl[18:19,26:27] *}
|
||||
{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *}
|
||||
|
||||
The same way, you could have some dependencies with `yield` and some other dependencies with `return`, and have some of those depend on some of the others.
|
||||
|
||||
@@ -109,7 +109,7 @@ But it's there for you if you need it. 🤓
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py39.py hl[18:22,31] *}
|
||||
{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *}
|
||||
|
||||
If you want to catch exceptions and create a custom response based on that, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
@@ -117,7 +117,7 @@ If you want to catch exceptions and create a custom response based on that, crea
|
||||
|
||||
If you catch an exception using `except` in a dependency with `yield` and you don't raise it again (or raise a new exception), FastAPI won't be able to notice there was an exception, the same way that would happen with regular Python:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py39.py hl[15:16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *}
|
||||
|
||||
In this case, the client will see an *HTTP 500 Internal Server Error* response as it should, given that we are not raising an `HTTPException` or similar, but the server will **not have any logs** or any other indication of what was the error. 😱
|
||||
|
||||
@@ -127,7 +127,7 @@ If you catch an exception in a dependency with `yield`, unless you are raising a
|
||||
|
||||
You can re-raise the same exception using `raise`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *}
|
||||
|
||||
Now the client will get the same *HTTP 500 Internal Server Error* response, but the server will have our custom `InternalError` in the logs. 😎
|
||||
|
||||
@@ -190,7 +190,7 @@ Normally the exit code of dependencies with `yield` is executed **after the resp
|
||||
|
||||
But if you know that you won't need to use the dependency after returning from the *path operation function*, you can use `Depends(scope="function")` to tell FastAPI that it should close the dependency after the *path operation function* returns, but **before** the **response is sent**.
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py39.py hl[12,16] *}
|
||||
{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *}
|
||||
|
||||
`Depends()` receives a `scope` parameter that can be:
|
||||
|
||||
@@ -269,7 +269,7 @@ In Python, you can create Context Managers by <a href="https://docs.python.org/3
|
||||
You can also use them inside of **FastAPI** dependencies with `yield` by using
|
||||
`with` or `async with` statements inside of the dependency function:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial010_py39.py hl[1:9,13] *}
|
||||
{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Similar to the way you can [add `dependencies` to the *path operation decorators
|
||||
|
||||
In that case, they will be applied to all the *path operations* in the application:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py39.py hl[17] *}
|
||||
{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *}
|
||||
|
||||
|
||||
And all the ideas in the section about [adding `dependencies` to the *path operation decorators*](dependencies-in-path-operation-decorators.md){.internal-link target=_blank} still apply, but in this case, to all of the *path operations* in the app.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Dependencies { #dependencies }
|
||||
|
||||
**FastAPI** has a very powerful but intuitive **<abbr title="also known as components, resources, providers, services, injectables">Dependency Injection</abbr>** system.
|
||||
**FastAPI** has a very powerful but intuitive **<dfn title="also known as components, resources, providers, services, injectables">Dependency Injection</dfn>** system.
|
||||
|
||||
It is designed to be very simple to use, and to make it very easy for any developer to integrate other components with **FastAPI**.
|
||||
|
||||
|
||||
@@ -58,11 +58,11 @@ query_extractor --> query_or_cookie_extractor --> read_query
|
||||
|
||||
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.
|
||||
|
||||
And it will save the returned value in a <abbr title="A utility/system to store computed/generated values, to reuse them instead of computing them again.">"cache"</abbr> and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request.
|
||||
And it will save the returned value in a <dfn title="A utility/system to store computed/generated values, to reuse them instead of computing them again.">"cache"</dfn> and pass it to all the "dependants" that need it in that specific request, instead of calling the dependency multiple times for the same request.
|
||||
|
||||
In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple times) in the same request instead of using the "cached" value, you can set the parameter `use_cache=False` when using `Depends`:
|
||||
|
||||
//// tab | Python 3.9+
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="1"
|
||||
async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):
|
||||
@@ -71,7 +71,7 @@ async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_ca
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -190,9 +190,9 @@ But if we put that in the assignment `response_model=PlaneItem | CarItem` we wou
|
||||
|
||||
The same way, you can declare responses of lists of objects.
|
||||
|
||||
For that, use the standard Python `typing.List` (or just `list` in Python 3.9 and above):
|
||||
For that, use the standard Python `list`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial004_py39.py hl[18] *}
|
||||
{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *}
|
||||
|
||||
## Response with arbitrary `dict` { #response-with-arbitrary-dict }
|
||||
|
||||
@@ -200,9 +200,9 @@ You can also declare a response using a plain arbitrary `dict`, declaring just t
|
||||
|
||||
This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.
|
||||
|
||||
In this case, you can use `typing.Dict` (or just `dict` in Python 3.9 and above):
|
||||
In this case, you can use `dict`:
|
||||
|
||||
{* ../../docs_src/extra_models/tutorial005_py39.py hl[6] *}
|
||||
{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *}
|
||||
|
||||
## Recap { #recap }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The simplest FastAPI file could look like this:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py *}
|
||||
|
||||
Copy that to a file `main.py`.
|
||||
|
||||
@@ -54,7 +54,7 @@ In the output, there's a line with something like:
|
||||
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
That line shows the URL where your app is being served, in your local machine.
|
||||
That line shows the URL where your app is being served on your local machine.
|
||||
|
||||
### Check it { #check-it }
|
||||
|
||||
@@ -183,7 +183,7 @@ That's it! Now you can access your app at that URL. ✨
|
||||
|
||||
### Step 1: import `FastAPI` { #step-1-import-fastapi }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *}
|
||||
|
||||
`FastAPI` is a Python class that provides all the functionality for your API.
|
||||
|
||||
@@ -197,7 +197,7 @@ You can use all the <a href="https://www.starlette.dev/" class="external-link" t
|
||||
|
||||
### Step 2: create a `FastAPI` "instance" { #step-2-create-a-fastapi-instance }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[3] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *}
|
||||
|
||||
Here the `app` variable will be an "instance" of the class `FastAPI`.
|
||||
|
||||
@@ -266,12 +266,12 @@ We are going to call them "**operations**" too.
|
||||
|
||||
#### Define a *path operation decorator* { #define-a-path-operation-decorator }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *}
|
||||
|
||||
The `@app.get("/")` tells **FastAPI** that the function right below is in charge of handling requests that go to:
|
||||
|
||||
* the path `/`
|
||||
* using a <abbr title="an HTTP GET method"><code>get</code> operation</abbr>
|
||||
* using a <dfn title="an HTTP GET method"><code>get</code> operation</dfn>
|
||||
|
||||
/// info | `@decorator` Info
|
||||
|
||||
@@ -320,7 +320,7 @@ This is our "**path operation function**":
|
||||
* **operation**: is `get`.
|
||||
* **function**: is the function below the "decorator" (below `@app.get("/")`).
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *}
|
||||
|
||||
This is a Python function.
|
||||
|
||||
@@ -332,7 +332,7 @@ In this case, it is an `async` function.
|
||||
|
||||
You could also define it as a normal function instead of `async def`:
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *}
|
||||
|
||||
/// note
|
||||
|
||||
@@ -342,7 +342,7 @@ If you don't know the difference, check the [Async: *"In a hurry?"*](../async.md
|
||||
|
||||
### Step 5: return the content { #step-5-return-the-content }
|
||||
|
||||
{* ../../docs_src/first_steps/tutorial001_py39.py hl[8] *}
|
||||
{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *}
|
||||
|
||||
You can return a `dict`, `list`, singular values as `str`, `int`, etc.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ To return HTTP responses with errors to the client you use `HTTPException`.
|
||||
|
||||
### Import `HTTPException` { #import-httpexception }
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[1] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *}
|
||||
|
||||
### Raise an `HTTPException` in your code { #raise-an-httpexception-in-your-code }
|
||||
|
||||
@@ -39,7 +39,7 @@ The benefit of raising an exception over returning a value will be more evident
|
||||
|
||||
In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a status code of `404`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial001_py39.py hl[11] *}
|
||||
{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *}
|
||||
|
||||
### The resulting response { #the-resulting-response }
|
||||
|
||||
@@ -77,7 +77,7 @@ You probably won't need to use it directly in your code.
|
||||
|
||||
But in case you needed it for an advanced scenario, you can add custom headers:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial002_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *}
|
||||
|
||||
## Install custom exception handlers { #install-custom-exception-handlers }
|
||||
|
||||
@@ -89,7 +89,7 @@ And you want to handle this exception globally with FastAPI.
|
||||
|
||||
You could add a custom exception handler with `@app.exception_handler()`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial003_py39.py hl[5:7,13:18,24] *}
|
||||
{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *}
|
||||
|
||||
Here, if you request `/unicorns/yolo`, the *path operation* will `raise` a `UnicornException`.
|
||||
|
||||
@@ -127,7 +127,7 @@ To override it, import the `RequestValidationError` and use it with `@app.except
|
||||
|
||||
The exception handler will receive a `Request` and the exception.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[2,14:19] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *}
|
||||
|
||||
Now, if you go to `/items/foo`, instead of getting the default JSON error with:
|
||||
|
||||
@@ -159,7 +159,7 @@ The same way, you can override the `HTTPException` handler.
|
||||
|
||||
For example, you could want to return a plain text response instead of JSON for these errors:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial004_py39.py hl[3:4,9:11,25] *}
|
||||
{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *}
|
||||
|
||||
/// note | Technical Details
|
||||
|
||||
@@ -183,7 +183,7 @@ The `RequestValidationError` contains the `body` it received with invalid data.
|
||||
|
||||
You could use it while developing your app to log the body and debug it, return it to the user, etc.
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial005_py39.py hl[14] *}
|
||||
{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *}
|
||||
|
||||
Now try sending an invalid item like:
|
||||
|
||||
@@ -239,6 +239,6 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
If you want to use the exception along with the same default exception handlers from **FastAPI**, you can import and reuse the default exception handlers from `fastapi.exception_handlers`:
|
||||
|
||||
{* ../../docs_src/handling_errors/tutorial006_py39.py hl[2:5,15,21] *}
|
||||
{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *}
|
||||
|
||||
In this example you are just printing the error with a very expressive message, but you get the idea. You can use the exception and then just reuse the default exception handlers.
|
||||
|
||||
@@ -18,7 +18,7 @@ You can set the following fields that are used in the OpenAPI specification and
|
||||
|
||||
You can set them as follows:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_py39.py hl[3:16, 19:32] *}
|
||||
{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -36,7 +36,7 @@ Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the `license_info` with
|
||||
|
||||
For example:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial001_1_py39.py hl[31] *}
|
||||
{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *}
|
||||
|
||||
## Metadata for tags { #metadata-for-tags }
|
||||
|
||||
@@ -58,7 +58,7 @@ Let's try that in an example with tags for `users` and `items`.
|
||||
|
||||
Create metadata for your tags and pass it to the `openapi_tags` parameter:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[3:16,18] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *}
|
||||
|
||||
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in bold (**login**) and "fancy" will be shown in italics (_fancy_).
|
||||
|
||||
@@ -72,7 +72,7 @@ You don't have to add metadata for all the tags that you use.
|
||||
|
||||
Use the `tags` parameter with your *path operations* (and `APIRouter`s) to assign them to different tags:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial004_py39.py hl[21,26] *}
|
||||
{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -100,7 +100,7 @@ But you can configure it with the parameter `openapi_url`.
|
||||
|
||||
For example, to set it to be served at `/api/v1/openapi.json`:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial002_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *}
|
||||
|
||||
If you want to disable the OpenAPI schema completely you can set `openapi_url=None`, that will also disable the documentation user interfaces that use it.
|
||||
|
||||
@@ -117,4 +117,4 @@ You can configure the two documentation user interfaces included:
|
||||
|
||||
For example, to set Swagger UI to be served at `/documentation` and disable ReDoc:
|
||||
|
||||
{* ../../docs_src/metadata/tutorial003_py39.py hl[3] *}
|
||||
{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *}
|
||||
|
||||
@@ -31,7 +31,7 @@ The middleware function receives:
|
||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||
* You can then further modify the `response` before returning it.
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[8:9,11,14] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -57,7 +57,7 @@ And also after the `response` is generated, before returning it.
|
||||
|
||||
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
|
||||
|
||||
{* ../../docs_src/middleware/tutorial001_py39.py hl[10,12:13] *}
|
||||
{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *}
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ In these cases, it could make sense to store the tags in an `Enum`.
|
||||
|
||||
**FastAPI** supports that the same way as with plain strings:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py39.py hl[1,8:10,13,18] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *}
|
||||
|
||||
## Summary and description { #summary-and-description }
|
||||
|
||||
@@ -56,7 +56,7 @@ You can add a `summary` and `description`:
|
||||
|
||||
## Description from docstring { #description-from-docstring }
|
||||
|
||||
As descriptions tend to be long and cover multiple lines, you can declare the *path operation* description in the function <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr> and **FastAPI** will read it from there.
|
||||
As descriptions tend to be long and cover multiple lines, you can declare the *path operation* description in the function <dfn title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</dfn> and **FastAPI** will read it from there.
|
||||
|
||||
You can write <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
|
||||
|
||||
@@ -90,9 +90,9 @@ So, if you don't provide one, **FastAPI** will automatically generate one of "Su
|
||||
|
||||
## Deprecate a *path operation* { #deprecate-a-path-operation }
|
||||
|
||||
If you need to mark a *path operation* as <abbr title="obsolete, recommended not to use it">deprecated</abbr>, but without removing it, pass the parameter `deprecated`:
|
||||
If you need to mark a *path operation* as <dfn title="obsolete, recommended not to use it">deprecated</dfn>, but without removing it, pass the parameter `deprecated`:
|
||||
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *}
|
||||
|
||||
It will be clearly marked as deprecated in the interactive docs:
|
||||
|
||||
|
||||
@@ -54,11 +54,11 @@ It doesn't matter for **FastAPI**. It will detect the parameters by their names,
|
||||
|
||||
So, you can declare your function as:
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *}
|
||||
|
||||
But keep in mind that if you use `Annotated`, you won't have this problem, it won't matter as you're not using the function parameter default values for `Query()` or `Path()`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py39.py *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *}
|
||||
|
||||
## Order the parameters as you need, tricks { #order-the-parameters-as-you-need-tricks }
|
||||
|
||||
@@ -83,13 +83,13 @@ Pass `*`, as the first parameter of the function.
|
||||
|
||||
Python won't do anything with that `*`, but it will know that all the following parameters should be called as keyword arguments (key-value pairs), also known as <abbr title="From: K-ey W-ord Arg-uments"><code>kwargs</code></abbr>. Even if they don't have a default value.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *}
|
||||
|
||||
### Better with `Annotated` { #better-with-annotated }
|
||||
|
||||
Keep in mind that if you use `Annotated`, as you are not using function parameter default values, you won't have this problem, and you probably won't need to use `*`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *}
|
||||
|
||||
## Number validations: greater than or equal { #number-validations-greater-than-or-equal }
|
||||
|
||||
@@ -97,7 +97,7 @@ With `Query` and `Path` (and others you'll see later) you can declare number con
|
||||
|
||||
Here, with `ge=1`, `item_id` will need to be an integer number "`g`reater than or `e`qual" to `1`.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *}
|
||||
|
||||
## Number validations: greater than and less than or equal { #number-validations-greater-than-and-less-than-or-equal }
|
||||
|
||||
@@ -106,7 +106,7 @@ The same applies for:
|
||||
* `gt`: `g`reater `t`han
|
||||
* `le`: `l`ess than or `e`qual
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py39.py hl[10] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *}
|
||||
|
||||
## Number validations: floats, greater than and less than { #number-validations-floats-greater-than-and-less-than }
|
||||
|
||||
@@ -118,7 +118,7 @@ So, `0.5` would be a valid value. But `0.0` or `0` would not.
|
||||
|
||||
And the same for <abbr title="less than"><code>lt</code></abbr>.
|
||||
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py39.py hl[13] *}
|
||||
{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *}
|
||||
|
||||
## Recap { #recap }
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial001_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *}
|
||||
|
||||
The value of the path parameter `item_id` will be passed to your function as the argument `item_id`.
|
||||
|
||||
@@ -16,7 +16,7 @@ So, if you run this example and go to <a href="http://127.0.0.1:8000/items/foo"
|
||||
|
||||
You can declare the type of a path parameter in the function, using standard Python type annotations:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial002_py39.py hl[7] *}
|
||||
{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *}
|
||||
|
||||
In this case, `item_id` is declared to be an `int`.
|
||||
|
||||
@@ -26,7 +26,7 @@ This will give you editor support inside of your function, with error checks, co
|
||||
|
||||
///
|
||||
|
||||
## Data <abbr title="also known as: serialization, parsing, marshalling">conversion</abbr> { #data-conversion }
|
||||
## Data <dfn title="also known as: serialization, parsing, marshalling">conversion</dfn> { #data-conversion }
|
||||
|
||||
If you run this example and open your browser at <a href="http://127.0.0.1:8000/items/3" class="external-link" target="_blank">http://127.0.0.1:8000/items/3</a>, you will see a response of:
|
||||
|
||||
@@ -38,7 +38,7 @@ If you run this example and open your browser at <a href="http://127.0.0.1:8000/
|
||||
|
||||
Notice that the value your function received (and returned) is `3`, as a Python `int`, not a string `"3"`.
|
||||
|
||||
So, with that type declaration, **FastAPI** gives you automatic request <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>.
|
||||
So, with that type declaration, **FastAPI** gives you automatic request <dfn title="converting the string that comes from an HTTP request into Python data">"parsing"</dfn>.
|
||||
|
||||
///
|
||||
|
||||
@@ -118,13 +118,13 @@ And then you can also have a path `/users/{user_id}` to get data about a specifi
|
||||
|
||||
Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *}
|
||||
|
||||
Otherwise, the path for `/users/{user_id}` would match also for `/users/me`, "thinking" that it's receiving a parameter `user_id` with a value of `"me"`.
|
||||
|
||||
Similarly, you cannot redefine a path operation:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial003b_py39.py hl[6,11] *}
|
||||
{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *}
|
||||
|
||||
The first one will always be used since the path matches first.
|
||||
|
||||
@@ -140,11 +140,11 @@ By inheriting from `str` the API docs will be able to know that the values must
|
||||
|
||||
Then create class attributes with fixed values, which will be the available valid values:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[1,6:9] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *}
|
||||
|
||||
/// tip
|
||||
|
||||
If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning <abbr title="Technically, Deep Learning model architectures">models</abbr>.
|
||||
If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning <dfn title="Technically, Deep Learning model architectures">models</dfn>.
|
||||
|
||||
///
|
||||
|
||||
@@ -152,7 +152,7 @@ If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine
|
||||
|
||||
Then create a *path parameter* with a type annotation using the enum class you created (`ModelName`):
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[16] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *}
|
||||
|
||||
### Check the docs { #check-the-docs }
|
||||
|
||||
@@ -168,13 +168,13 @@ The value of the *path parameter* will be an *enumeration member*.
|
||||
|
||||
You can compare it with the *enumeration member* in your created enum `ModelName`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[17] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *}
|
||||
|
||||
#### Get the *enumeration value* { #get-the-enumeration-value }
|
||||
|
||||
You can get the actual value (a `str` in this case) using `model_name.value`, or in general, `your_enum_member.value`:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[20] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -188,7 +188,7 @@ You can return *enum members* from your *path operation*, even nested in a JSON
|
||||
|
||||
They will be converted to their corresponding values (strings in this case) before returning them to the client:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial005_py39.py hl[18,21,23] *}
|
||||
{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *}
|
||||
|
||||
In your client you will get a JSON response like:
|
||||
|
||||
@@ -227,7 +227,7 @@ In this case, the name of the parameter is `file_path`, and the last part, `:pat
|
||||
|
||||
So, you can use it with:
|
||||
|
||||
{* ../../docs_src/path_params/tutorial004_py39.py hl[6] *}
|
||||
{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -242,7 +242,7 @@ In that case, the URL would be: `/files//home/johndoe/myfile.txt`, with a double
|
||||
With **FastAPI**, by using short, intuitive and standard Python type declarations, you get:
|
||||
|
||||
* Editor support: error checks, autocompletion, etc.
|
||||
* Data "<abbr title="converting the string that comes from an HTTP request into Python data">parsing</abbr>"
|
||||
* Data "<dfn title="converting the string that comes from an HTTP request into Python data">parsing</dfn>"
|
||||
* Data validation
|
||||
* API annotation and automatic documentation
|
||||
|
||||
|
||||
@@ -47,40 +47,16 @@ Now it's the time to use it with FastAPI. 🚀
|
||||
|
||||
We had this type annotation:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: str | None = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Union[str, None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
What we will do is wrap that with `Annotated`, so it becomes:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python
|
||||
q: Annotated[str | None] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python
|
||||
q: Annotated[Union[str, None]] = None
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
Both of those versions mean the same thing, `q` is a parameter that can be a `str` or `None`, and by default, it is `None`.
|
||||
|
||||
Now let's jump to the fun stuff. 🎉
|
||||
@@ -109,7 +85,7 @@ FastAPI will now:
|
||||
|
||||
## Alternative (old): `Query` as the default value { #alternative-old-query-as-the-default-value }
|
||||
|
||||
Previous versions of FastAPI (before <abbr title="before 2023-03">0.95.0</abbr>) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you.
|
||||
Previous versions of FastAPI (before <dfn title="before 2023-03">0.95.0</dfn>) required you to use `Query` as the default value of your parameter, instead of putting it in `Annotated`, there's a high chance that you will see code using it around, so I'll explain it to you.
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -192,7 +168,7 @@ You can also add a parameter `min_length`:
|
||||
|
||||
## Add regular expressions { #add-regular-expressions }
|
||||
|
||||
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> `pattern` that the parameter should match:
|
||||
You can define a <dfn title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</dfn> `pattern` that the parameter should match:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *}
|
||||
|
||||
@@ -212,7 +188,7 @@ You can, of course, use default values other than `None`.
|
||||
|
||||
Let's say that you want to declare the `q` query parameter to have a `min_length` of `3`, and to have a default value of `"fixedquery"`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *}
|
||||
|
||||
/// note
|
||||
|
||||
@@ -242,7 +218,7 @@ q: Annotated[str | None, Query(min_length=3)] = None
|
||||
|
||||
So, when you need to declare a value as required while using `Query`, you can simply not declare a default value:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *}
|
||||
|
||||
### Required, can be `None` { #required-can-be-none }
|
||||
|
||||
@@ -293,7 +269,7 @@ The interactive API docs will update accordingly, to allow multiple values:
|
||||
|
||||
You can also define a default `list` of values if none are provided:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *}
|
||||
|
||||
If you go to:
|
||||
|
||||
@@ -316,7 +292,7 @@ the default of `q` will be: `["foo", "bar"]` and your response will be:
|
||||
|
||||
You can also use `list` directly instead of `list[str]`:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *}
|
||||
|
||||
/// note
|
||||
|
||||
@@ -372,7 +348,7 @@ Then you can declare an `alias`, and that alias is what will be used to find the
|
||||
|
||||
Now let's say you don't like this parameter anymore.
|
||||
|
||||
You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as <abbr title="obsolete, recommended not to use it">deprecated</abbr>.
|
||||
You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as <dfn title="obsolete, recommended not to use it">deprecated</dfn>.
|
||||
|
||||
Then pass the parameter `deprecated=True` to `Query`:
|
||||
|
||||
@@ -402,7 +378,7 @@ Pydantic also has <a href="https://docs.pydantic.dev/latest/concepts/validators/
|
||||
|
||||
///
|
||||
|
||||
For example, this custom validator checks that the item ID starts with `isbn-` for an <abbr title="ISBN means International Standard Book Number">ISBN</abbr> book number or with `imdb-` for an <abbr title="IMDB (Internet Movie Database) is a website with information about movies">IMDB</abbr> movie URL ID:
|
||||
For example, this custom validator checks that the item ID starts with `isbn-` for an <abbr title="International Standard Book Number">ISBN</abbr> book number or with `imdb-` for an <abbr title="Internet Movie Database: a website with information about movies">IMDB</abbr> movie URL ID:
|
||||
|
||||
{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *}
|
||||
|
||||
@@ -436,7 +412,7 @@ Did you notice? a string using `value.startswith()` can take a tuple, and it wil
|
||||
|
||||
#### A Random Item { #a-random-item }
|
||||
|
||||
With `data.items()` we get an <abbr title="Something we can iterate on with a for loop, like a list, set, etc.">iterable object</abbr> with tuples containing the key and value for each dictionary item.
|
||||
With `data.items()` we get an <dfn title="Something we can iterate on with a for loop, like a list, set, etc.">iterable object</dfn> with tuples containing the key and value for each dictionary item.
|
||||
|
||||
We convert this iterable object into a proper `list` with `list(data.items())`.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.
|
||||
|
||||
{* ../../docs_src/query_params/tutorial001_py39.py hl[9] *}
|
||||
{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *}
|
||||
|
||||
The query is the set of key-value pairs that go after the `?` in a URL, separated by `&` characters.
|
||||
|
||||
@@ -24,7 +24,7 @@ But when you declare them with Python types (in the example above, as `int`), th
|
||||
All the same process that applied for path parameters also applies for query parameters:
|
||||
|
||||
* Editor support (obviously)
|
||||
* Data <abbr title="converting the string that comes from an HTTP request into Python data">"parsing"</abbr>
|
||||
* Data <dfn title="converting the string that comes from an HTTP request into Python data">"parsing"</dfn>
|
||||
* Data validation
|
||||
* Automatic documentation
|
||||
|
||||
@@ -128,7 +128,7 @@ If you don't want to add a specific value but just make it optional, set the def
|
||||
|
||||
But when you want to make a query parameter required, you can just not declare any default value:
|
||||
|
||||
{* ../../docs_src/query_params/tutorial005_py39.py hl[6:7] *}
|
||||
{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *}
|
||||
|
||||
Here the query parameter `needy` is a required query parameter of type `str`.
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ This is because uploaded files are sent as "form data".
|
||||
|
||||
Import `File` and `UploadFile` from `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Define `File` Parameters { #define-file-parameters }
|
||||
|
||||
Create file parameters the same way you would for `Body` or `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
/// info
|
||||
|
||||
@@ -54,7 +54,7 @@ But there are several cases in which you might benefit from using `UploadFile`.
|
||||
|
||||
Define a file parameter with a type of `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *}
|
||||
|
||||
Using `UploadFile` has several advantages over `bytes`:
|
||||
|
||||
@@ -143,7 +143,7 @@ You can make a file optional by using standard type annotations and setting a de
|
||||
|
||||
You can also use `File()` with `UploadFile`, for example, to set additional metadata:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *}
|
||||
|
||||
## Multiple File Uploads { #multiple-file-uploads }
|
||||
|
||||
@@ -153,7 +153,7 @@ They would be associated to the same "form field" sent using "form data".
|
||||
|
||||
To use that, declare a list of `bytes` or `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *}
|
||||
|
||||
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||
|
||||
@@ -169,7 +169,7 @@ You could also use `from starlette.responses import HTMLResponse`.
|
||||
|
||||
And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *}
|
||||
|
||||
## Recap { #recap }
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ This is supported since FastAPI version `0.113.0`. 🤓
|
||||
|
||||
You just need to declare a **Pydantic model** with the fields you want to receive as **form fields**, and then declare the parameter as `Form`:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py39.py hl[9:11,15] *}
|
||||
{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *}
|
||||
|
||||
**FastAPI** will **extract** the data for **each field** from the **form data** in the request and give you the Pydantic model you defined.
|
||||
|
||||
@@ -48,7 +48,7 @@ This is supported since FastAPI version `0.114.0`. 🤓
|
||||
|
||||
You can use Pydantic's model configuration to `forbid` any `extra` fields:
|
||||
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *}
|
||||
|
||||
If a client tries to send some extra data, they will receive an **error** response.
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ $ pip install python-multipart
|
||||
|
||||
## Import `File` and `Form` { #import-file-and-form }
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Define `File` and `Form` parameters { #define-file-and-form-parameters }
|
||||
|
||||
Create file and form parameters the same way you would for `Body` or `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py39.py hl[10:12] *}
|
||||
{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *}
|
||||
|
||||
The files and form fields will be uploaded as form data and you will receive the files and form fields.
|
||||
|
||||
|
||||
@@ -18,17 +18,17 @@ $ pip install python-multipart
|
||||
|
||||
Import `Form` from `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[3] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *}
|
||||
|
||||
## Define `Form` parameters { #define-form-parameters }
|
||||
|
||||
Create form parameters the same way you would for `Body` or `Query`:
|
||||
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py39.py hl[9] *}
|
||||
{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *}
|
||||
|
||||
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is required to send a `username` and `password` as form fields.
|
||||
|
||||
The <abbr title="specification">spec</abbr> requires the fields to be exactly named `username` and `password`, and to be sent as form fields, not JSON.
|
||||
The <dfn title="specification">spec</dfn> requires the fields to be exactly named `username` and `password`, and to be sent as form fields, not JSON.
|
||||
|
||||
With `Form` you can declare the same configurations as with `Body` (and `Query`, `Path`, `Cookie`), including validation, examples, an alias (e.g. `user-name` instead of `username`), etc.
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ There might be cases where you return something that is not a valid Pydantic fie
|
||||
|
||||
The most common case would be [returning a Response directly as explained later in the advanced docs](../advanced/response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_02_py39.py hl[8,10:11] *}
|
||||
{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *}
|
||||
|
||||
This simple case is handled automatically by FastAPI because the return type annotation is the class (or a subclass of) `Response`.
|
||||
|
||||
@@ -193,7 +193,7 @@ And tools will also be happy because both `RedirectResponse` and `JSONResponse`
|
||||
|
||||
You can also use a subclass of `Response` in the type annotation:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_03_py39.py hl[8:9] *}
|
||||
{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *}
|
||||
|
||||
This will also work because `RedirectResponse` is a subclass of `Response`, and FastAPI will automatically handle this simple case.
|
||||
|
||||
@@ -201,7 +201,7 @@ This will also work because `RedirectResponse` is a subclass of `Response`, and
|
||||
|
||||
But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object) and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that type annotation, and will fail.
|
||||
|
||||
The same would happen if you had something like a <abbr title='A union between multiple types means "any of these types".'>union</abbr> between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:
|
||||
The same would happen if you had something like a <dfn title='A union between multiple types means "any of these types".'>union</dfn> between different types where one or more of them are not valid Pydantic types, for example this would fail 💥:
|
||||
|
||||
{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ The same way you can specify a response model, you can also declare the HTTP sta
|
||||
* `@app.delete()`
|
||||
* etc.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
/// note
|
||||
|
||||
@@ -74,7 +74,7 @@ To know more about each status code and which code is for what, check the <a hre
|
||||
|
||||
Let's see the previous example again:
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial001_py39.py hl[6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *}
|
||||
|
||||
`201` is the status code for "Created".
|
||||
|
||||
@@ -82,7 +82,7 @@ But you don't have to memorize what each of these codes mean.
|
||||
|
||||
You can use the convenience variables from `fastapi.status`.
|
||||
|
||||
{* ../../docs_src/response_status_code/tutorial002_py39.py hl[1,6] *}
|
||||
{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *}
|
||||
|
||||
They are just a convenience, they hold the same number, but that way you can use the editor's autocomplete to find them:
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ You can of course also pass multiple `examples`:
|
||||
|
||||
When you do this, the examples will be part of the internal **JSON Schema** for that body data.
|
||||
|
||||
Nevertheless, at the <abbr title="2023-08-26">time of writing this</abbr>, Swagger UI, the tool in charge of showing the docs UI, doesn't support showing multiple examples for the data in **JSON Schema**. But read below for a workaround.
|
||||
Nevertheless, at the <dfn title="2023-08-26">time of writing this</dfn>, Swagger UI, the tool in charge of showing the docs UI, doesn't support showing multiple examples for the data in **JSON Schema**. But read below for a workaround.
|
||||
|
||||
### OpenAPI-specific `examples` { #openapi-specific-examples }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Let's first just use the code and see how it works, and then we'll come back to
|
||||
|
||||
Copy the example in a file `main.py`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py *}
|
||||
|
||||
## Run it { #run-it }
|
||||
|
||||
@@ -132,7 +132,7 @@ In that case, **FastAPI** also provides you with the tools to build it.
|
||||
|
||||
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[8] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -170,7 +170,7 @@ So, it can be used with `Depends`.
|
||||
|
||||
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
In the previous chapter the security system (which is based on the dependency injection system) was giving the *path operation function* a `token` as a `str`:
|
||||
|
||||
{* ../../docs_src/security/tutorial001_an_py39.py hl[12] *}
|
||||
{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *}
|
||||
|
||||
But that is still not that useful.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ You can serve static files automatically from a directory using `StaticFiles`.
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
{* ../../docs_src/static_files/tutorial001_py39.py hl[2,6] *}
|
||||
{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *}
|
||||
|
||||
/// note | Technical Details
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Use the `TestClient` object the same way as you do with `httpx`.
|
||||
|
||||
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial001_py39.py hl[2,12,15:18] *}
|
||||
{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -76,7 +76,7 @@ Let's say you have a file structure as described in [Bigger Applications](bigger
|
||||
In the file `main.py` you have your **FastAPI** app:
|
||||
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/main.py *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/main.py *}
|
||||
|
||||
### Testing file { #testing-file }
|
||||
|
||||
@@ -92,7 +92,7 @@ Then you could have a file `test_main.py` with your tests. It could live on the
|
||||
|
||||
Because this file is in the same package, you can use relative imports to import the object `app` from the `main` module (`main.py`):
|
||||
|
||||
{* ../../docs_src/app_testing/app_a_py39/test_main.py hl[3] *}
|
||||
{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *}
|
||||
|
||||
|
||||
...and have the code for the tests just like before.
|
||||
|
||||
@@ -53,7 +53,7 @@ $ cd awesome-project
|
||||
|
||||
## Create a Virtual Environment { #create-a-virtual-environment }
|
||||
|
||||
When you start working on a Python project **for the first time**, create a virtual environment **<abbr title="there are other options, this is a simple guideline">inside your project</abbr>**.
|
||||
When you start working on a Python project **for the first time**, create a virtual environment **<dfn title="there are other options, this is a simple guideline">inside your project</dfn>**.
|
||||
|
||||
/// tip
|
||||
|
||||
|
||||
@@ -191,6 +191,7 @@ nav:
|
||||
- advanced/openapi-webhooks.md
|
||||
- advanced/wsgi.md
|
||||
- advanced/generate-clients.md
|
||||
- advanced/advanced-python-types.md
|
||||
- fastapi-cli.md
|
||||
- Deployment:
|
||||
- deployment/index.md
|
||||
@@ -331,6 +332,8 @@ extra:
|
||||
name: tr - Türkçe
|
||||
- link: /uk/
|
||||
name: uk - українська мова
|
||||
- link: /zh/
|
||||
name: zh - 简体中文
|
||||
- link: /zh-hant/
|
||||
name: zh-hant - 繁體中文
|
||||
extra_css:
|
||||
|
||||
@@ -202,15 +202,10 @@ Aquí algunas cosas envueltas en elementos HTML "abbr" (algunas son inventadas):
|
||||
* <abbr title="XML Web Token - Token web XML">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Interfaz de pasarela de servidor paralela">PSGI</abbr>
|
||||
|
||||
### El abbr da una explicación { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="Un grupo de máquinas configuradas para estar conectadas y trabajar juntas de alguna manera.">clúster</abbr>
|
||||
* <abbr title="Un método de machine learning que usa redes neuronales artificiales con numerosas capas ocultas entre las capas de entrada y salida, desarrollando así una estructura interna completa">Deep Learning</abbr>
|
||||
|
||||
### El abbr da una frase completa y una explicación { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Red de Desarrolladores de Mozilla: documentación para desarrolladores, escrita por la gente de Firefox">MDN</abbr>
|
||||
* <abbr title="Input/Output: lectura o escritura de disco, comunicaciones de red.">I/O</abbr>.
|
||||
* <abbr title="Input/Output - Entrada/Salida: lectura o escritura de disco, comunicaciones de red.">I/O</abbr>.
|
||||
|
||||
////
|
||||
|
||||
@@ -224,6 +219,11 @@ Consulta la sección `### HTML abbr elements` en el prompt general en `scripts/t
|
||||
|
||||
////
|
||||
|
||||
## Elementos HTML "dfn" { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Un grupo de máquinas configuradas para estar conectadas y trabajar juntas de alguna manera.">clúster</dfn>
|
||||
* <dfn title="Un método de Machine Learning que usa redes neuronales artificiales con numerosas capas ocultas entre las capas de entrada y salida, desarrollando así una estructura interna completa">Deep Learning</dfn>
|
||||
|
||||
## Encabezados { #headings }
|
||||
|
||||
//// tab | Prueba
|
||||
@@ -433,7 +433,7 @@ Para instrucciones específicas del idioma, mira p. ej. la sección `### Heading
|
||||
* el motor de plantillas
|
||||
|
||||
* la anotación de tipos
|
||||
* las anotaciones de tipos
|
||||
* la anotación de tipos
|
||||
|
||||
* el worker del servidor
|
||||
* el worker de Uvicorn
|
||||
|
||||
@@ -132,7 +132,7 @@ works(foo="bar") # 이건 동작합니다 🎉
|
||||
일부 텍스트
|
||||
///
|
||||
|
||||
/// note Technical details | 기술 세부사항
|
||||
/// note | 기술 세부사항
|
||||
일부 텍스트
|
||||
///
|
||||
|
||||
@@ -202,11 +202,6 @@ works(foo="bar") # 이건 동작합니다 🎉
|
||||
* <abbr title="XML Web Token - XML 웹 토큰">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - 병렬 서버 게이트웨이 인터페이스">PSGI</abbr>
|
||||
|
||||
### abbr가 설명을 제공 { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="어떤 방식으로든 서로 연결되고 함께 작동하도록 구성된 머신들의 집합입니다.">cluster</abbr>
|
||||
* <abbr title="입력과 출력 계층 사이에 수많은 은닉 계층을 둔 인공 신경망을 사용하는 머신 러닝 방법으로, 이를 통해 포괄적인 내부 구조를 형성합니다">Deep Learning</abbr>
|
||||
|
||||
### abbr가 전체 문구와 설명을 제공 { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - 모질라 개발자 네트워크: Firefox를 만드는 사람들이 작성한 개발자용 문서">MDN</abbr>
|
||||
@@ -224,6 +219,11 @@ works(foo="bar") # 이건 동작합니다 🎉
|
||||
|
||||
////
|
||||
|
||||
## HTML "dfn" 요소 { #html-dfn-elements }
|
||||
|
||||
* <dfn title="어떤 방식으로든 서로 연결되고 함께 작동하도록 구성된 머신들의 집합입니다.">클러스터</dfn>
|
||||
* <dfn title="입력과 출력 계층 사이에 수많은 은닉 계층을 둔 인공 신경망을 사용하는 머신 러닝 방법으로, 이를 통해 포괄적인 내부 구조를 형성합니다">딥 러닝</dfn>
|
||||
|
||||
## 제목 { #headings }
|
||||
|
||||
//// tab | 테스트
|
||||
|
||||
@@ -197,15 +197,10 @@ Aqui estão algumas coisas envolvidas em elementos HTML "abbr" (algumas são inv
|
||||
|
||||
### O abbr fornece uma frase completa { #the-abbr-gives-a-full-phrase }
|
||||
|
||||
* <abbr title="Getting Things Done">GTD</abbr>
|
||||
* <abbr title="less than - menos que"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Interface de Gateway de Servidor Paralelo">PSGI</abbr>
|
||||
|
||||
### O abbr fornece uma explicação { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="Um grupo de máquinas configuradas para estarem conectadas e trabalharem juntas de alguma forma.">cluster</abbr>
|
||||
* <abbr title="Um método de aprendizado de máquina que usa redes neurais artificiais com numerosas camadas ocultas entre as camadas de entrada e saída, desenvolvendo assim uma estrutura interna abrangente">Deep Learning</abbr>
|
||||
* <abbr title="Getting Things Done – Fazer as Coisas">GTD</abbr>
|
||||
* <abbr title="less than – menos que"><code>lt</code></abbr>
|
||||
* <abbr title="XML Web Token – Token Web XML">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface – Interface de Gateway de Servidor Paralelo">PSGI</abbr>
|
||||
|
||||
### O abbr fornece uma frase completa e uma explicação { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
@@ -224,6 +219,11 @@ Veja a seção `### HTML abbr elements` no prompt geral em `scripts/translate.py
|
||||
|
||||
////
|
||||
|
||||
## Elementos HTML "dfn" { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Um grupo de máquinas configuradas para estarem conectadas e trabalharem juntas de alguma forma.">cluster</dfn>
|
||||
* <dfn title="Um método de aprendizado de máquina que usa redes neurais artificiais com numerosas camadas ocultas entre as camadas de entrada e saída, desenvolvendo assim uma estrutura interna abrangente">Deep Learning</dfn>
|
||||
|
||||
## Títulos { #headings }
|
||||
|
||||
//// tab | Teste
|
||||
|
||||
@@ -202,11 +202,6 @@ works(foo="bar") # Это работает 🎉
|
||||
* <abbr title="XML Web Token - XML веб‑токен">XWT</abbr>
|
||||
* <abbr title="Parallel Server Gateway Interface - Параллельный серверный интерфейс шлюза">PSGI</abbr>
|
||||
|
||||
### abbr даёт объяснение { #the-abbr-gives-an-explanation }
|
||||
|
||||
* <abbr title="Группа машин, которые настроены на соединение и совместную работу определённым образом.">кластер</abbr>
|
||||
* <abbr title="Метод машинного обучения, который использует искусственные нейронные сети с многочисленными скрытыми слоями между входным и выходным слоями, тем самым формируя сложную внутреннюю структуру">Глубокое обучение</abbr>
|
||||
|
||||
### abbr даёт полную расшифровку и объяснение { #the-abbr-gives-a-full-phrase-and-an-explanation }
|
||||
|
||||
* <abbr title="Mozilla Developer Network - Сеть разработчиков Mozilla: документация для разработчиков, созданная командой Firefox">MDN</abbr>
|
||||
@@ -224,6 +219,11 @@ works(foo="bar") # Это работает 🎉
|
||||
|
||||
////
|
||||
|
||||
## HTML-элементы "dfn" { #html-dfn-elements }
|
||||
|
||||
* <dfn title="Группа машин, которые настроены на соединение и совместную работу определённым образом.">кластер</dfn>
|
||||
* <dfn title="Метод машинного обучения, который использует искусственные нейронные сети с многочисленными скрытыми слоями между входным и выходным слоями, тем самым формируя сложную внутреннюю структуру">Глубокое обучение</dfn>
|
||||
|
||||
## Заголовки { #headings }
|
||||
|
||||
//// tab | Тест
|
||||
|
||||
@@ -1,44 +1,57 @@
|
||||
# OPENAPI 中的其他响应
|
||||
# OpenAPI 中的附加响应 { #additional-responses-in-openapi }
|
||||
|
||||
您可以声明附加响应,包括附加状态代码、媒体类型、描述等。
|
||||
/// warning | 警告
|
||||
|
||||
这些额外的响应将包含在OpenAPI模式中,因此它们也将出现在API文档中。
|
||||
这是一个相对高级的话题。
|
||||
|
||||
但是对于那些额外的响应,你必须确保你直接返回一个像 `JSONResponse` 一样的 `Response` ,并包含你的状态代码和内容。
|
||||
|
||||
## `model`附加响应
|
||||
您可以向路径操作装饰器传递参数 `responses` 。
|
||||
|
||||
它接收一个 `dict`,键是每个响应的状态代码(如`200`),值是包含每个响应信息的其他 `dict`。
|
||||
|
||||
每个响应字典都可以有一个关键模型,其中包含一个 `Pydantic` 模型,就像 `response_model` 一样。
|
||||
|
||||
**FastAPI**将采用该模型,生成其`JSON Schema`并将其包含在`OpenAPI`中的正确位置。
|
||||
|
||||
例如,要声明另一个具有状态码 `404` 和`Pydantic`模型 `Message` 的响应,可以写:
|
||||
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
|
||||
|
||||
/// note
|
||||
|
||||
请记住,您必须直接返回 `JSONResponse` 。
|
||||
如果你刚开始使用 **FastAPI**,可能暂时用不到。
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
你可以声明附加响应,包括额外的状态码、媒体类型、描述等。
|
||||
|
||||
`model` 密钥不是OpenAPI的一部分。
|
||||
**FastAPI**将从那里获取`Pydantic`模型,生成` JSON Schema` ,并将其放在正确的位置。
|
||||
- 正确的位置是:
|
||||
- 在键 `content` 中,其具有另一个`JSON`对象( `dict` )作为值,该`JSON`对象包含:
|
||||
- 媒体类型的密钥,例如 `application/json` ,它包含另一个`JSON`对象作为值,该对象包含:
|
||||
- 一个键` schema` ,它的值是来自模型的`JSON Schema`,正确的位置在这里。
|
||||
- **FastAPI**在这里添加了对OpenAPI中另一个地方的全局JSON模式的引用,而不是直接包含它。这样,其他应用程序和客户端可以直接使用这些JSON模式,提供更好的代码生成工具等。
|
||||
这些附加响应会被包含在 OpenAPI 模式中,因此它们也会出现在 API 文档中。
|
||||
|
||||
但是对于这些附加响应,你必须确保直接返回一个 `Response`(例如 `JSONResponse`),并携带你的状态码和内容。
|
||||
|
||||
## 带有 `model` 的附加响应 { #additional-response-with-model }
|
||||
|
||||
你可以向你的*路径操作装饰器*传入参数 `responses`。
|
||||
|
||||
它接收一个 `dict`:键是每个响应的状态码(例如 `200`),值是包含该响应信息的另一个 `dict`。
|
||||
|
||||
这些响应的每个 `dict` 都可以有一个键 `model`,包含一个 Pydantic 模型,就像 `response_model` 一样。
|
||||
|
||||
**FastAPI** 会获取该模型,生成它的 JSON Schema,并将其放在 OpenAPI 中的正确位置。
|
||||
|
||||
例如,要声明另一个状态码为 `404` 且具有 Pydantic 模型 `Message` 的响应,你可以这样写:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
|
||||
/// note | 注意
|
||||
|
||||
记住你需要直接返回 `JSONResponse`。
|
||||
|
||||
///
|
||||
|
||||
**在OpenAPI中为该路径操作生成的响应将是:**
|
||||
/// info | 信息
|
||||
|
||||
```json hl_lines="3-12"
|
||||
`model` 键不是 OpenAPI 的一部分。
|
||||
|
||||
**FastAPI** 会从这里获取 Pydantic 模型,生成 JSON Schema,并把它放到正确的位置。
|
||||
|
||||
正确的位置是:
|
||||
|
||||
* 在键 `content` 中,它的值是另一个 JSON 对象(`dict`),该对象包含:
|
||||
* 一个媒体类型作为键,例如 `application/json`,它的值是另一个 JSON 对象,该对象包含:
|
||||
* 一个键 `schema`,它的值是来自该模型的 JSON Schema,这里就是正确的位置。
|
||||
* **FastAPI** 会在这里添加一个引用,指向你 OpenAPI 中另一个位置的全局 JSON Schemas,而不是直接内联。这样,其他应用和客户端可以直接使用这些 JSON Schemas,提供更好的代码生成工具等。
|
||||
|
||||
///
|
||||
|
||||
为该*路径操作*在 OpenAPI 中生成的响应将是:
|
||||
|
||||
```JSON hl_lines="3-12"
|
||||
{
|
||||
"responses": {
|
||||
"404": {
|
||||
@@ -73,10 +86,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
**模式被引用到OpenAPI模式中的另一个位置:**
|
||||
```json hl_lines="4-16"
|
||||
|
||||
这些模式在 OpenAPI 模式中被引用到另一个位置:
|
||||
|
||||
```JSON hl_lines="4-16"
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
@@ -153,48 +167,54 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
## 主响应的其他媒体类型
|
||||
|
||||
您可以使用相同的 `responses` 参数为相同的主响应添加不同的媒体类型。
|
||||
## 主响应的其他媒体类型 { #additional-media-types-for-the-main-response }
|
||||
|
||||
例如,您可以添加一个额外的媒体类型` image/png` ,声明您的路径操作可以返回JSON对象(媒体类型 `application/json` )或PNG图像:
|
||||
你可以使用同一个 `responses` 参数为同一个主响应添加不同的媒体类型。
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
|
||||
例如,你可以添加一个额外的媒体类型 `image/png`,声明你的*路径操作*可以返回 JSON 对象(媒体类型为 `application/json`)或 PNG 图片:
|
||||
|
||||
/// note
|
||||
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
|
||||
|
||||
- 请注意,您必须直接使用 `FileResponse` 返回图像。
|
||||
/// note | 注意
|
||||
|
||||
请注意,你必须直接使用 `FileResponse` 返回图片。
|
||||
|
||||
///
|
||||
|
||||
/// info
|
||||
/// info | 信息
|
||||
|
||||
- 除非在 `responses` 参数中明确指定不同的媒体类型,否则**FastAPI**将假定响应与主响应类具有相同的媒体类型(默认为` application/json` )。
|
||||
- 但是如果您指定了一个自定义响应类,并将 `None `作为其媒体类型,**FastAPI**将使用 `application/json` 作为具有关联模型的任何其他响应。
|
||||
除非你在 `responses` 参数中明确指定不同的媒体类型,否则 FastAPI 会假设响应与主响应类具有相同的媒体类型(默认是 `application/json`)。
|
||||
|
||||
但是如果你指定了一个媒体类型为 `None` 的自定义响应类,FastAPI 会对任何具有关联模型的附加响应使用 `application/json`。
|
||||
|
||||
///
|
||||
|
||||
## 组合信息
|
||||
您还可以联合接收来自多个位置的响应信息,包括 `response_model `、 `status_code` 和 `responses `参数。
|
||||
## 组合信息 { #combining-information }
|
||||
|
||||
您可以使用默认的状态码 `200` (或者您需要的自定义状态码)声明一个 `response_model `,然后直接在OpenAPI模式中在 `responses` 中声明相同响应的其他信息。
|
||||
你也可以把来自多个位置的响应信息组合在一起,包括 `response_model`、`status_code` 和 `responses` 参数。
|
||||
|
||||
**FastAPI**将保留来自 `responses` 的附加信息,并将其与模型中的JSON Schema结合起来。
|
||||
你可以声明一个 `response_model`,使用默认状态码 `200`(或根据需要使用自定义状态码),然后在 `responses` 中直接在 OpenAPI 模式里为同一个响应声明附加信息。
|
||||
|
||||
例如,您可以使用状态码 `404` 声明响应,该响应使用`Pydantic`模型并具有自定义的` description` 。
|
||||
**FastAPI** 会保留来自 `responses` 的附加信息,并把它与你的模型生成的 JSON Schema 合并。
|
||||
|
||||
以及一个状态码为 `200` 的响应,它使用您的 `response_model` ,但包含自定义的 `example` :
|
||||
例如,你可以声明一个状态码为 `404` 的响应,它使用一个 Pydantic 模型并带有自定义的 `description`。
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
|
||||
以及一个状态码为 `200` 的响应,它使用你的 `response_model`,但包含自定义的 `example`:
|
||||
|
||||
所有这些都将被合并并包含在您的OpenAPI中,并在API文档中显示:
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
|
||||
## 联合预定义响应和自定义响应
|
||||
所有这些都会被合并并包含到你的 OpenAPI 中,并显示在 API 文档里:
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
## 组合预定义响应和自定义响应 { #combine-predefined-responses-and-custom-ones }
|
||||
|
||||
你可能希望有一些适用于许多*路径操作*的预定义响应,但同时又想把它们与每个*路径操作*所需的自定义响应组合在一起。
|
||||
|
||||
在这些情况下,你可以使用 Python 的“解包”`dict` 的技巧 `**dict_to_unpack`:
|
||||
|
||||
您可能希望有一些应用于许多路径操作的预定义响应,但是你想将不同的路径和自定义的相应组合在一块。
|
||||
对于这些情况,你可以使用Python的技术,将 `dict` 与 `**dict_to_unpack` 解包:
|
||||
```Python
|
||||
old_dict = {
|
||||
"old key": "old value",
|
||||
@@ -203,19 +223,25 @@ old_dict = {
|
||||
new_dict = {**old_dict, "new key": "new value"}
|
||||
```
|
||||
|
||||
这里, new_dict 将包含来自 old_dict 的所有键值对加上新的键值对:
|
||||
```python
|
||||
这里,`new_dict` 将包含来自 `old_dict` 的所有键值对,再加上新的键值对:
|
||||
|
||||
```Python
|
||||
{
|
||||
"old key": "old value",
|
||||
"second old key": "second old value",
|
||||
"new key": "new value",
|
||||
}
|
||||
```
|
||||
您可以使用该技术在路径操作中重用一些预定义的响应,并将它们与其他自定义响应相结合。
|
||||
**例如:**
|
||||
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
|
||||
## 有关OpenAPI响应的更多信息
|
||||
|
||||
要了解您可以在响应中包含哪些内容,您可以查看OpenAPI规范中的以下部分:
|
||||
+ [OpenAPI响应对象](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responsesObject),它包括 Response Object 。
|
||||
+ [OpenAPI响应对象](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responseObject),您可以直接在 `responses` 参数中的每个响应中包含任何内容。包括 `description` 、 `headers` 、 `content` (其中是声明不同的媒体类型和JSON Schemas)和 `links` 。
|
||||
你可以使用该技巧在*路径操作*中重用一些预定义响应,并把它们与额外的自定义响应组合在一起。
|
||||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
|
||||
|
||||
## 关于 OpenAPI 响应的更多信息 { #more-information-about-openapi-responses }
|
||||
|
||||
要查看响应中究竟可以包含什么,你可以查看 OpenAPI 规范中的以下部分:
|
||||
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object" class="external-link" target="_blank">OpenAPI Responses 对象</a>,它包含 `Response Object`。
|
||||
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object" class="external-link" target="_blank">OpenAPI Response 对象</a>,你可以把这里的任何内容直接包含到 `responses` 参数中的每个响应里。包括 `description`、`headers`、`content`(在这里声明不同的媒体类型和 JSON Schemas),以及 `links`。
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 额外的状态码
|
||||
# 额外的状态码 { #additional-status-codes }
|
||||
|
||||
**FastAPI** 默认使用 `JSONResponse` 返回一个响应,将你的 *路径操作* 中的返回内容放到该 `JSONResponse` 中。
|
||||
|
||||
**FastAPI** 会自动使用默认的状态码或者使用你在 *路径操作* 中设置的状态码。
|
||||
|
||||
## 额外的状态码
|
||||
## 额外的状态码 { #additional-status-codes_1 }
|
||||
|
||||
如果你想要返回主要状态码之外的状态码,你可以通过直接返回一个 `Response` 来实现,比如 `JSONResponse`,然后直接设置额外的状态码。
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
|
||||
但是你也希望它能够接受新的条目。并且当这些条目不存在时,会自动创建并返回 201 「创建」的 HTTP 状态码。
|
||||
|
||||
要实现它,导入 `JSONResponse`,然后在其中直接返回你的内容,并将 `status_code` 设置为为你要的值。
|
||||
要实现它,导入 `JSONResponse`,然后在其中直接返回你的内容,并将 `status_code` 设置为你要的值。
|
||||
|
||||
{* ../../docs_src/additional_status_codes/tutorial001.py hl[4,25] *}
|
||||
{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *}
|
||||
|
||||
/// warning | 警告
|
||||
/// warning
|
||||
|
||||
当你直接返回一个像上面例子中的 `Response` 对象时,它会直接返回。
|
||||
|
||||
FastAPI 不会用模型等对该响应进行序列化。
|
||||
它不会用模型等进行序列化。
|
||||
|
||||
确保其中有你想要的数据,且返回的值为合法的 JSON(如果你使用 `JSONResponse` 的话)。
|
||||
|
||||
@@ -34,7 +34,7 @@ FastAPI 不会用模型等对该响应进行序列化。
|
||||
|
||||
///
|
||||
|
||||
## OpenAPI 和 API 文档
|
||||
## OpenAPI 和 API 文档 { #openapi-and-api-docs }
|
||||
|
||||
如果你直接返回额外的状态码和响应,它们不会包含在 OpenAPI 方案(API 文档)中,因为 FastAPI 没办法预先知道你要返回什么。
|
||||
|
||||
|
||||
@@ -1,65 +1,163 @@
|
||||
# 高级依赖项
|
||||
# 高级依赖项 { #advanced-dependencies }
|
||||
|
||||
## 参数化的依赖项
|
||||
## 参数化的依赖项 { #parameterized-dependencies }
|
||||
|
||||
我们之前看到的所有依赖项都是写死的函数或类。
|
||||
目前我们看到的依赖项都是固定的函数或类。
|
||||
|
||||
但也可以为依赖项设置参数,避免声明多个不同的函数或类。
|
||||
但有时你可能希望为依赖项设置参数,而不必声明许多不同的函数或类。
|
||||
|
||||
假设要创建校验查询参数 `q` 是否包含固定内容的依赖项。
|
||||
假设我们要有一个依赖项,用来检查查询参数 `q` 是否包含某个固定内容。
|
||||
|
||||
但此处要把待检验的固定内容定义为参数。
|
||||
但我们希望能够把这个固定内容参数化。
|
||||
|
||||
## **可调用**实例
|
||||
## “可调用”的实例 { #a-callable-instance }
|
||||
|
||||
Python 可以把类实例变为**可调用项**。
|
||||
在 Python 中,可以让某个类的实例变成“可调用对象”(callable)。
|
||||
|
||||
这里说的不是类本身(类本就是可调用项),而是类实例。
|
||||
这里指的是类的实例(类本身已经是可调用的),而不是类本身。
|
||||
|
||||
为此,需要声明 `__call__` 方法:
|
||||
为此,声明一个 `__call__` 方法:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011.py hl[10] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[12] *}
|
||||
|
||||
本例中,**FastAPI** 使用 `__call__` 检查附加参数及子依赖项,稍后,还要调用它向*路径操作函数*传递值。
|
||||
在这种情况下,**FastAPI** 会使用这个 `__call__` 来检查附加参数和子依赖,并且稍后会调用它,把返回值传递给你的*路径操作函数*中的参数。
|
||||
|
||||
## 参数化实例
|
||||
## 参数化实例 { #parameterize-the-instance }
|
||||
|
||||
接下来,使用 `__init__` 声明用于**参数化**依赖项的实例参数:
|
||||
现在,我们可以用 `__init__` 声明实例的参数,用来“参数化”这个依赖项:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011.py hl[7] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[9] *}
|
||||
|
||||
本例中,**FastAPI** 不使用 `__init__`,我们要直接在代码中使用。
|
||||
在本例中,**FastAPI** 不会接触或关心 `__init__`,我们会在自己的代码中直接使用它。
|
||||
|
||||
## 创建实例
|
||||
## 创建实例 { #create-an-instance }
|
||||
|
||||
使用以下代码创建类实例:
|
||||
我们可以这样创建该类的实例:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011.py hl[16] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[18] *}
|
||||
|
||||
这样就可以**参数化**依赖项,它包含 `checker.fixed_content` 的属性 - `"bar"`。
|
||||
这样就把依赖项“参数化”了,现在它内部带有属性 `checker.fixed_content` 的值 `"bar"`。
|
||||
|
||||
## 把实例作为依赖项
|
||||
## 把实例作为依赖项 { #use-the-instance-as-a-dependency }
|
||||
|
||||
然后,不要再在 `Depends(checker)` 中使用 `Depends(FixedContentQueryChecker)`, 而是要使用 `checker`,因为依赖项是类实例 - `checker`,不是类。
|
||||
然后,我们可以在 `Depends(checker)` 中使用这个 `checker`,而不是 `Depends(FixedContentQueryChecker)`,因为依赖项是实例 `checker`,不是类本身。
|
||||
|
||||
处理依赖项时,**FastAPI** 以如下方式调用 `checker`:
|
||||
解析依赖项时,**FastAPI** 会像这样调用 `checker`:
|
||||
|
||||
```Python
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
……并用*路径操作函数*的参数 `fixed_content_included` 返回依赖项的值:
|
||||
...并将其返回值作为依赖项的值,传给我们的*路径操作函数*中的参数 `fixed_content_included`:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial011.py hl[20] *}
|
||||
{* ../../docs_src/dependencies/tutorial011_an_py39.py hl[22] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
本章示例有些刻意,也看不出有什么用处。
|
||||
这些看起来可能有些牵强,目前它的用处也许还不太明显。
|
||||
|
||||
这个简例只是为了说明高级依赖项的运作机制。
|
||||
这些示例刻意保持简单,但展示了整体的工作方式。
|
||||
|
||||
在有关安全的章节中,工具函数将以这种方式实现。
|
||||
在安全相关的章节里,有一些工具函数就是以相同的方式实现的。
|
||||
|
||||
只要能理解本章内容,就能理解安全工具背后的运行机制。
|
||||
如果你理解了这里的内容,你就已经知道那些安全工具在底层是如何工作的。
|
||||
|
||||
///
|
||||
|
||||
## 带 `yield` 的依赖项、`HTTPException`、`except` 与后台任务 { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
你很可能不需要了解这些技术细节。
|
||||
|
||||
这些细节主要在你的 FastAPI 应用版本低于 0.121.0 且你正遇到带 `yield` 的依赖项问题时才有用。
|
||||
|
||||
///
|
||||
|
||||
带 `yield` 的依赖项随着时间演进以覆盖不同用例并修复一些问题,下面是变更摘要。
|
||||
|
||||
### 带 `yield` 的依赖项与 `scope` { #dependencies-with-yield-and-scope }
|
||||
|
||||
在 0.121.0 版本中,FastAPI 为带 `yield` 的依赖项新增了 `Depends(scope="function")` 的支持。
|
||||
|
||||
使用 `Depends(scope="function")` 时,`yield` 之后的退出代码会在*路径操作函数*结束后、响应发送给客户端之前立即执行。
|
||||
|
||||
而当使用默认的 `Depends(scope="request")` 时,`yield` 之后的退出代码会在响应发送之后执行。
|
||||
|
||||
你可以在文档 [带 `yield` 的依赖项 - 提前退出与 `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) 中了解更多。
|
||||
|
||||
### 带 `yield` 的依赖项与 `StreamingResponse`(技术细节) { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
在 FastAPI 0.118.0 之前,如果你使用带 `yield` 的依赖项,它会在*路径操作函数*返回后、发送响应之前运行 `yield` 之后的退出代码。
|
||||
|
||||
这样做的目的是避免在等待响应通过网络传输期间不必要地占用资源。
|
||||
|
||||
这也意味着,如果你返回的是 `StreamingResponse`,那么该带 `yield` 的依赖项的退出代码会在开始发送响应前就已经执行完毕。
|
||||
|
||||
例如,如果你在带 `yield` 的依赖项中持有一个数据库会话,那么 `StreamingResponse` 在流式发送数据时将无法使用该会话,因为会话已经在 `yield` 之后的退出代码里被关闭了。
|
||||
|
||||
在 0.118.0 中,这一行为被回退为:让 `yield` 之后的退出代码在响应发送之后再执行。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
如你在下文所见,这与 0.106.0 之前的行为非常相似,但对若干边界情况做了改进和修复。
|
||||
|
||||
///
|
||||
|
||||
#### 需要提前执行退出代码的用例 { #use-cases-with-early-exit-code }
|
||||
|
||||
在某些特定条件下,旧的行为(在发送响应之前执行带 `yield` 依赖项的退出代码)会更有利。
|
||||
|
||||
例如,设想你在带 `yield` 的依赖项中仅用数据库会话来校验用户,而在*路径操作函数*中并不会再次使用该会话;同时,响应需要很长时间才能发送完,比如一个缓慢发送数据的 `StreamingResponse`,且它出于某种原因并不使用数据库。
|
||||
|
||||
这种情况下,会一直持有数据库会话直到响应发送完毕;但如果并不再使用它,就没有必要一直占用。
|
||||
|
||||
代码可能如下:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
|
||||
|
||||
退出代码(自动关闭 `Session`)位于:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
|
||||
|
||||
...会在响应把慢速数据发送完之后才运行:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
|
||||
|
||||
但由于 `generate_stream()` 并不使用数据库会话,因此在发送响应期间保持会话打开并非必要。
|
||||
|
||||
如果你使用的是 SQLModel(或 SQLAlchemy)并碰到这种特定用例,你可以在不再需要时显式关闭会话:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
|
||||
|
||||
这样会话会释放数据库连接,让其他请求可以使用。
|
||||
|
||||
如果你还有其他需要在 `yield` 依赖项中提前退出的用例,请创建一个 <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">GitHub 讨论问题</a>,说明你的具体用例以及为何提前关闭会对你有帮助。
|
||||
|
||||
如果确有有力的用例需要提前关闭,我会考虑新增一种选择性启用提前关闭的方式。
|
||||
|
||||
### 带 `yield` 的依赖项与 `except`(技术细节) { #dependencies-with-yield-and-except-technical-details }
|
||||
|
||||
在 FastAPI 0.110.0 之前,如果你在带 `yield` 的依赖项中用 `except` 捕获了一个异常,并且没有再次抛出它,那么该异常会被自动抛出/转发给任意异常处理器或内部服务器错误处理器。
|
||||
|
||||
在 0.110.0 中对此作出了变更,以修复将异常转发为未处理(内部服务器错误)时造成的内存消耗问题,并使其与常规 Python 代码的行为保持一致。
|
||||
|
||||
### 后台任务与带 `yield` 的依赖项(技术细节) { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
在 FastAPI 0.106.0 之前,`yield` 之后抛出异常是不可行的,因为带 `yield` 的依赖项中的退出代码会在响应发送之后才执行,此时[异常处理器](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}已经运行完毕。
|
||||
|
||||
之所以这样设计,主要是为了允许在后台任务中继续使用依赖项通过 `yield`“产出”的对象,因为退出代码会在后台任务完成之后才执行。
|
||||
|
||||
在 FastAPI 0.106.0 中,这一行为被修改,目的是避免在等待响应通过网络传输时一直占用资源。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
另外,后台任务通常是一段独立的逻辑,应该单独处理,并使用它自己的资源(例如它自己的数据库连接)。
|
||||
|
||||
因此,这样做你的代码通常会更清晰。
|
||||
|
||||
///
|
||||
|
||||
如果你过去依赖于旧行为,现在应在后台任务内部自行创建所需资源,并且只在内部使用不依赖于带 `yield` 依赖项资源的数据。
|
||||
|
||||
例如,不要复用相同的数据库会话,而是在后台任务内部创建一个新的会话,并用这个新会话从数据库获取对象。然后,不是把数据库对象本身作为参数传给后台任务函数,而是传递该对象的 ID,并在后台任务函数内部再次获取该对象。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 异步测试
|
||||
# 异步测试 { #async-tests }
|
||||
|
||||
您已经了解了如何使用 `TestClient` 测试 **FastAPI** 应用程序。但是到目前为止,您只了解了如何编写同步测试,而没有使用 `async` 异步函数。
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
让我们看看如何才能实现这一点。
|
||||
|
||||
## pytest.mark.anyio
|
||||
## pytest.mark.anyio { #pytest-mark-anyio }
|
||||
|
||||
如果我们想在测试中调用异步函数,那么我们的测试函数必须是异步的。 AnyIO 为此提供了一个简洁的插件,它允许我们指定一些测试函数要异步调用。
|
||||
|
||||
## HTTPX
|
||||
## HTTPX { #httpx }
|
||||
|
||||
即使您的 **FastAPI** 应用程序使用普通的 `def` 函数而不是 `async def` ,它本质上仍是一个 `async` 异步应用程序。
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
`TestClient` 是基于 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> 的。幸运的是,我们可以直接使用它来测试API。
|
||||
|
||||
## 示例
|
||||
## 示例 { #example }
|
||||
|
||||
举个简单的例子,让我们来看一个[更大的应用](../tutorial/bigger-applications.md){.internal-link target=_blank}和[测试](../tutorial/testing.md){.internal-link target=_blank}中描述的类似文件结构:
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
|
||||
文件 `main.py` 将包含:
|
||||
|
||||
{* ../../docs_src/async_tests/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
|
||||
文件 `test_main.py` 将包含针对 `main.py` 的测试,现在它可能看起来如下:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
|
||||
## 运行测试
|
||||
## 运行测试 { #run-it }
|
||||
|
||||
您可以通过以下方式照常运行测试:
|
||||
|
||||
@@ -52,13 +52,13 @@ $ pytest
|
||||
|
||||
</div>
|
||||
|
||||
## 详细说明
|
||||
## 详细说明 { #in-detail }
|
||||
|
||||
这个标记 `@pytest.mark.anyio` 会告诉 pytest 该测试函数应该被异步调用:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
请注意,测试函数现在用的是 `async def`,而不是像以前使用 `TestClient` 时那样只是 `def` 。
|
||||
|
||||
@@ -66,7 +66,7 @@ $ pytest
|
||||
|
||||
我们现在可以使用应用程序创建一个 `AsyncClient` ,并使用 `await` 向其发送异步请求。
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
|
||||
这相当于:
|
||||
|
||||
@@ -76,24 +76,24 @@ response = client.get('/')
|
||||
|
||||
我们曾经通过它向 `TestClient` 发出请求。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
请注意,我们正在将 async/await 与新的 `AsyncClient` 一起使用——请求是异步的。
|
||||
|
||||
///
|
||||
|
||||
/// warning
|
||||
/// warning | 警告
|
||||
|
||||
如果您的应用程序依赖于生命周期事件, `AsyncClient` 将不会触发这些事件。为了确保它们被触发,请使用 <a href="https://github.com/florimondmanca/asgi-lifespan#usage" class="external-link" target="_blank">florimondmanca/asgi-lifespan</a> 中的 `LifespanManager` 。
|
||||
|
||||
///
|
||||
|
||||
## 其他异步函数调用
|
||||
## 其他异步函数调用 { #other-asynchronous-function-calls }
|
||||
|
||||
由于测试函数现在是异步的,因此除了在测试中向 FastAPI 应用程序发送请求之外,您现在还可以调用(和使用 `await` 等待)其他 `async` 异步函数,就和您在代码中的其他任何地方调用它们的方法一样。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
如果您在测试程序中集成异步函数调用的时候遇到一个 `RuntimeError: Task attached to a different loop` 的报错(例如,使用 <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB 的 MotorClient</a> 时),请记住,只能在异步函数中实例化需要事件循环的对象,例如通过 `'@app.on_event("startup")` 回调函数进行初始化。
|
||||
如果您在测试程序中集成异步函数调用的时候遇到一个 `RuntimeError: Task attached to a different loop` 的报错(例如,使用 <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB 的 MotorClient</a> 时),请记住,只能在异步函数中实例化需要事件循环的对象,例如在 `@app.on_event("startup")` 回调中初始化。
|
||||
|
||||
///
|
||||
|
||||
@@ -1,30 +1,131 @@
|
||||
# 使用代理
|
||||
# 使用代理 { #behind-a-proxy }
|
||||
|
||||
有些情况下,您可能要使用 Traefik 或 Nginx 等**代理**服务器,并添加应用不能识别的附加路径前缀配置。
|
||||
在很多情况下,你会在 FastAPI 应用前面使用像 Traefik 或 Nginx 这样的**代理**。
|
||||
|
||||
此时,要使用 `root_path` 配置应用。
|
||||
这些代理可以处理 HTTPS 证书等事项。
|
||||
|
||||
`root_path` 是 ASGI 规范提供的机制,FastAPI 就是基于此规范开发的(通过 Starlette)。
|
||||
## 代理转发的请求头 { #proxy-forwarded-headers }
|
||||
|
||||
在你的应用前面的**代理**通常会在把请求转发给你的**服务器**之前,临时设置一些请求头,让服务器知道该请求是由代理**转发**的,并告知原始(公网)URL,包括域名、是否使用 HTTPS 等。
|
||||
|
||||
**服务器**程序(例如通过 **FastAPI CLI** 运行的 **Uvicorn**)能够解析这些请求头,然后把这些信息传递给你的应用。
|
||||
|
||||
但出于安全考虑,由于服务器并不知道自己处在受信任的代理之后,它默认不会解析这些请求头。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
这些代理相关的请求头包括:
|
||||
|
||||
- <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
- <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
- <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
### 启用代理转发的请求头 { #enable-proxy-forwarded-headers }
|
||||
|
||||
你可以用 *CLI 选项* `--forwarded-allow-ips` 启动 FastAPI CLI,并传入应该被信任、允许读取这些转发请求头的 IP 地址列表。
|
||||
|
||||
如果设置为 `--forwarded-allow-ips="*"`,就会信任所有来源 IP。
|
||||
|
||||
如果你的**服务器**位于受信任的**代理**之后,并且只有代理会与它通信,这将使其接受该**代理**的任何 IP。
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### 使用 HTTPS 的重定向 { #redirects-with-https }
|
||||
|
||||
例如,假设你定义了一个*路径操作* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
|
||||
如果客户端尝试访问 `/items`,默认会被重定向到 `/items/`。
|
||||
|
||||
但在设置 *CLI 选项* `--forwarded-allow-ips` 之前,它可能会重定向到 `http://localhost:8000/items/`。
|
||||
|
||||
而你的应用可能托管在 `https://mysuperapp.com`,重定向应当是 `https://mysuperapp.com/items/`。
|
||||
|
||||
通过设置 `--proxy-headers`,FastAPI 现在就可以重定向到正确的位置。😎
|
||||
|
||||
```
|
||||
https://mysuperapp.com/items/
|
||||
```
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
如果你想了解更多关于 HTTPS 的内容,查看指南:[关于 HTTPS](../deployment/https.md){.internal-link target=_blank}。
|
||||
|
||||
///
|
||||
|
||||
### 代理转发请求头如何工作 { #how-proxy-forwarded-headers-work }
|
||||
|
||||
下面是一个可视化图示,展示了**代理**如何在客户端与**应用服务器**之间添加转发请求头:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Proxy as Proxy/Load Balancer
|
||||
participant Server as FastAPI Server
|
||||
|
||||
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Proxy: Proxy adds forwarded headers
|
||||
|
||||
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
|
||||
|
||||
Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
|
||||
|
||||
Proxy->>Client: HTTPS Response
|
||||
```
|
||||
|
||||
**代理**会拦截原始客户端请求,并在将请求传递给**应用服务器**之前,添加特殊的*转发*请求头(`X-Forwarded-*`)。
|
||||
|
||||
这些请求头保留了原始请求中否则会丢失的信息:
|
||||
|
||||
- X-Forwarded-For:原始客户端的 IP 地址
|
||||
- X-Forwarded-Proto:原始协议(`https`)
|
||||
- X-Forwarded-Host:原始主机(`mysuperapp.com`)
|
||||
|
||||
当 **FastAPI CLI** 配置了 `--forwarded-allow-ips` 后,它会信任并使用这些请求头,例如用于在重定向中生成正确的 URL。
|
||||
|
||||
## 移除路径前缀的代理 { #proxy-with-a-stripped-path-prefix }
|
||||
|
||||
你可能会有一个代理,为你的应用添加一个路径前缀。
|
||||
|
||||
在这些情况下,你可以使用 `root_path` 来配置你的应用。
|
||||
|
||||
`root_path` 是 ASGI 规范(FastAPI 基于该规范,通过 Starlette 构建)提供的机制。
|
||||
|
||||
`root_path` 用于处理这些特定情况。
|
||||
|
||||
在挂载子应用时,也可以在内部使用。
|
||||
在挂载子应用时,它也会在内部使用。
|
||||
|
||||
## 移除路径前缀的代理
|
||||
“移除路径前缀的代理”在这里的意思是:你可以在代码中声明一个路径 `/app`,然后在顶层添加一层(代理),把你的 **FastAPI** 应用放在类似 `/api/v1` 的路径下。
|
||||
|
||||
本例中,移除路径前缀的代理是指在代码中声明路径 `/app`,然后在应用顶层添加代理,把 **FastAPI** 应用放在 `/api/v1` 路径下。
|
||||
在这种情况下,原始路径 `/app` 实际上会在 `/api/v1/app` 提供服务。
|
||||
|
||||
本例的原始路径 `/app` 实际上是在 `/api/v1/app` 提供服务。
|
||||
即使你的所有代码都假设只有 `/app`。
|
||||
|
||||
哪怕所有代码都假设只有 `/app`。
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
|
||||
代理只在把请求传送给 Uvicorn 之前才会**移除路径前缀**,让应用以为它是在 `/app` 提供服务,因此不必在代码中加入前缀 `/api/v1`。
|
||||
代理会在将请求传递给应用服务器(可能是通过 FastAPI CLI 运行的 Uvicorn)之前,实时**“移除”**这个**路径前缀**,让你的应用认为它是在 `/app` 被服务,这样你就不需要更新所有代码去包含 `/api/v1` 前缀。
|
||||
|
||||
但之后,在(前端)打开 API 文档时,代理会要求在 `/openapi.json`,而不是 `/api/v1/openapi.json` 中提取 OpenAPI 概图。
|
||||
到这里,一切都会像往常一样工作。
|
||||
|
||||
因此, (运行在浏览器中的)前端会尝试访问 `/openapi.json`,但没有办法获取 OpenAPI 概图。
|
||||
但是,当你打开集成的文档界面(前端)时,它会期望在 `/openapi.json` 获取 OpenAPI 模式,而不是在 `/api/v1/openapi.json`。
|
||||
|
||||
这是因为应用使用了以 `/api/v1` 为路径前缀的代理,前端要从 `/api/v1/openapi.json` 中提取 OpenAPI 概图。
|
||||
因此,(在浏览器中运行的)前端会尝试访问 `/openapi.json`,但无法获取 OpenAPI 模式。
|
||||
|
||||
因为我们的应用使用了路径前缀为 `/api/v1` 的代理,前端需要从 `/api/v1/openapi.json` 获取 OpenAPI 模式。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
@@ -39,15 +140,15 @@ proxy --> server
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
IP `0.0.0.0` 常用于指程序监听本机或服务器上的所有有效 IP。
|
||||
IP `0.0.0.0` 通常表示程序监听该机器/服务器上的所有可用 IP。
|
||||
|
||||
///
|
||||
|
||||
API 文档还需要 OpenAPI 概图声明 API `server` 位于 `/api/v1`(使用代理时的 URL)。例如:
|
||||
文档界面还需要 OpenAPI 模式声明该 API 的 `server` 位于 `/api/v1`(代理后面)。例如:
|
||||
|
||||
```JSON hl_lines="4-8"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"openapi": "3.1.0",
|
||||
// More stuff here
|
||||
"servers": [
|
||||
{
|
||||
@@ -60,53 +161,53 @@ API 文档还需要 OpenAPI 概图声明 API `server` 位于 `/api/v1`(使用
|
||||
}
|
||||
```
|
||||
|
||||
本例中的 `Proxy` 是 **Traefik**,`server` 是运行 FastAPI 应用的 **Uvicorn**。
|
||||
在此示例中,“Proxy” 可以是 **Traefik** 之类的。服务器可以是用 **Uvicorn** 的 **FastAPI CLI** 运行你的 FastAPI 应用。
|
||||
|
||||
### 提供 `root_path`
|
||||
### 提供 `root_path` { #providing-the-root-path }
|
||||
|
||||
为此,要以如下方式使用命令行选项 `--root-path`:
|
||||
为此,你可以像下面这样使用命令行选项 `--root-path`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Hypercorn 也支持 `--root-path `选项。
|
||||
如果你使用 Hypercorn,它也有 `--root-path` 选项。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
ASGI 规范定义的 `root_path` 就是为了这种用例。
|
||||
ASGI 规范为这种用例定义了 `root_path`。
|
||||
|
||||
并且 `--root-path` 命令行选项支持 `root_path`。
|
||||
命令行选项 `--root-path` 会提供该 `root_path`。
|
||||
|
||||
///
|
||||
|
||||
### 查看当前的 `root_path`
|
||||
### 查看当前的 `root_path` { #checking-the-current-root-path }
|
||||
|
||||
获取应用为每个请求使用的当前 `root_path`,这是 `scope` 字典的内容(也是 ASGI 规范的内容)。
|
||||
你可以获取应用在每个请求中使用的当前 `root_path`,它是 `scope` 字典的一部分(ASGI 规范的一部分)。
|
||||
|
||||
我们在这里的信息里包含 `roo_path` 只是为了演示。
|
||||
这里我们把它包含在响应消息中仅用于演示。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
|
||||
然后,用以下命令启动 Uvicorn:
|
||||
然后,如果你这样启动 Uvicorn:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
返回的响应如下:
|
||||
响应类似于:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -115,19 +216,19 @@ $ uvicorn main:app --root-path /api/v1
|
||||
}
|
||||
```
|
||||
|
||||
### 在 FastAPI 应用里设置 `root_path`
|
||||
### 在 FastAPI 应用中设置 `root_path` { #setting-the-root-path-in-the-fastapi-app }
|
||||
|
||||
还有一种方案,如果不能提供 `--root-path` 或等效的命令行选项,则在创建 FastAPI 应用时要设置 `root_path` 参数。
|
||||
或者,如果你无法提供类似 `--root-path` 的命令行选项,你可以在创建 FastAPI 应用时设置参数 `root_path`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
|
||||
传递 `root_path` 给 `FastAPI` 与传递 `--root-path` 命令行选项给 Uvicorn 或 Hypercorn 一样。
|
||||
把 `root_path` 传给 `FastAPI` 等同于把命令行选项 `--root-path` 传给 Uvicorn 或 Hypercorn。
|
||||
|
||||
### 关于 `root_path`
|
||||
### 关于 `root_path` { #about-root-path }
|
||||
|
||||
注意,服务器(Uvicorn)只是把 `root_path` 传递给应用。
|
||||
请注意,服务器(Uvicorn)不会用这个 `root_path` 做别的事情,只会把它传给应用。
|
||||
|
||||
在浏览器中输入 <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app 时能看到标准响应:</a>
|
||||
但是,如果你用浏览器打开 <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>,你会看到正常的响应:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -136,25 +237,25 @@ $ uvicorn main:app --root-path /api/v1
|
||||
}
|
||||
```
|
||||
|
||||
它不要求访问 `http://127.0.0.1:800/api/v1/app`。
|
||||
因此,它不会期望被访问于 `http://127.0.0.1:8000/api/v1/app`。
|
||||
|
||||
Uvicorn 预期代理在 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶部添加 `/api/v1` 前缀是代理要做的事情。
|
||||
Uvicorn 会期望代理以 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶部额外添加 `/api/v1` 前缀是代理的职责。
|
||||
|
||||
## 关于移除路径前缀的代理
|
||||
## 关于移除路径前缀的代理 { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
注意,移除路径前缀的代理只是配置代理的方式之一。
|
||||
请记住,移除路径前缀只是配置代理的一种方式。
|
||||
|
||||
大部分情况下,代理默认都不会移除路径前缀。
|
||||
在很多情况下,默认是代理不会移除路径前缀。
|
||||
|
||||
(未移除路径前缀时)代理监听 `https://myawesomeapp.com` 等对象,如果浏览器跳转到 `https://myawesomeapp.com/api/v1/app`,且服务器(例如 Uvicorn)监听 `http://127.0.0.1:8000` 代理(未移除路径前缀) 会在同样的路径:`http://127.0.0.1:8000/api/v1/app` 访问 Uvicorn。
|
||||
在这种情况下(没有移除路径前缀),代理会监听类似 `https://myawesomeapp.com`,当浏览器访问 `https://myawesomeapp.com/api/v1/app` 且你的服务器(例如 Uvicorn)监听 `http://127.0.0.1:8000` 时,代理(未移除路径前缀)会以相同路径访问 Uvicorn:`http://127.0.0.1:8000/api/v1/app`。
|
||||
|
||||
## 本地测试 Traefik
|
||||
## 使用 Traefik 进行本地测试 { #testing-locally-with-traefik }
|
||||
|
||||
您可以轻易地在本地使用 <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a> 运行移除路径前缀的试验。
|
||||
你可以很容易地使用 <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a> 在本地运行一个移除路径前缀的实验。
|
||||
|
||||
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">下载 Traefik</a>,这是一个二进制文件,需要解压文件,并在 Terminal 中直接运行。
|
||||
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">下载 Traefik</a>,它是一个单独的二进制文件,你可以解压压缩包并直接在终端中运行。
|
||||
|
||||
然后创建包含如下内容的 `traefik.toml` 文件:
|
||||
然后创建一个 `traefik.toml` 文件,内容如下:
|
||||
|
||||
```TOML hl_lines="3"
|
||||
[entryPoints]
|
||||
@@ -166,15 +267,15 @@ Uvicorn 预期代理在 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶
|
||||
filename = "routes.toml"
|
||||
```
|
||||
|
||||
这个文件把 Traefik 监听端口设置为 `9999`,并设置要使用另一个文件 `routes.toml`。
|
||||
这告诉 Traefik 监听端口 9999,并使用另一个文件 `routes.toml`。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
使用端口 9999 代替标准的 HTTP 端口 80,这样就不必使用管理员权限运行(`sudo`)。
|
||||
我们使用 9999 端口而不是标准 HTTP 端口 80,这样你就不需要用管理员(`sudo`)权限运行。
|
||||
|
||||
///
|
||||
|
||||
接下来,创建 `routes.toml`:
|
||||
现在创建另一个文件 `routes.toml`:
|
||||
|
||||
```TOML hl_lines="5 12 20"
|
||||
[http]
|
||||
@@ -201,9 +302,9 @@ Uvicorn 预期代理在 `http://127.0.0.1:8000/app` 访问 Uvicorn,而在顶
|
||||
|
||||
这个文件配置 Traefik 使用路径前缀 `/api/v1`。
|
||||
|
||||
然后,它把请求重定位到运行在 `http://127.0.0.1:8000` 上的 Uvicorn。
|
||||
随后 Traefik 会把请求转发到运行在 `http://127.0.0.1:8000` 的 Uvicorn。
|
||||
|
||||
现在,启动 Traefik:
|
||||
现在启动 Traefik:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -215,21 +316,21 @@ INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
|
||||
|
||||
</div>
|
||||
|
||||
接下来,使用 Uvicorn 启动应用,并使用 `--root-path` 选项:
|
||||
然后使用 `--root-path` 选项启动你的应用:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ uvicorn main:app --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### 查看响应
|
||||
### 查看响应 { #check-the-responses }
|
||||
|
||||
访问含 Uvicorn 端口的 URL:<a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app,就能看到标准响应:</a>
|
||||
现在,如果你访问 Uvicorn 端口对应的 URL:<a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>,你会看到正常响应:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -240,13 +341,13 @@ $ uvicorn main:app --root-path /api/v1
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意,就算访问 `http://127.0.0.1:8000/app`,也显示从选项 `--root-path` 中提取的 `/api/v1`,这是 `root_path` 的值。
|
||||
注意,尽管你是通过 `http://127.0.0.1:8000/app` 访问,它仍显示 `root_path` 为 `/api/v1`,该值来自 `--root-path` 选项。
|
||||
|
||||
///
|
||||
|
||||
打开含 Traefik 端口的 URL,包含路径前缀:<a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app。</a>
|
||||
现在打开包含路径前缀、使用 Traefik 端口的 URL:<a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>。
|
||||
|
||||
得到同样的响应:
|
||||
我们得到相同的响应:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -255,57 +356,57 @@ $ uvicorn main:app --root-path /api/v1
|
||||
}
|
||||
```
|
||||
|
||||
但这一次 URL 包含了代理提供的路径前缀:`/api/v1`。
|
||||
但这次 URL 中带有代理提供的前缀路径:`/api/v1`。
|
||||
|
||||
当然,这是通过代理访问应用的方式,因此,路径前缀 `/app/v1` 版本才是**正确**的。
|
||||
当然,这里的想法是每个人都通过代理访问应用,因此带有路径前缀 `/api/v1` 的版本才是“正确”的。
|
||||
|
||||
而不带路径前缀的版本(`http://127.0.0.1:8000/app`),则由 Uvicorn 直接提供,专供*代理*(Traefik)访问。
|
||||
而不带路径前缀的版本(`http://127.0.0.1:8000/app`)由 Uvicorn 直接提供,仅供_代理_(Traefik)访问。
|
||||
|
||||
这演示了代理(Traefik)如何使用路径前缀,以及服务器(Uvicorn)如何使用选项 `--root-path` 中的 `root_path`。
|
||||
这说明了代理(Traefik)如何使用路径前缀,以及服务器(Uvicorn)如何使用 `--root-path` 选项提供的 `root_path`。
|
||||
|
||||
### 查看文档
|
||||
### 查看文档界面 { #check-the-docs-ui }
|
||||
|
||||
但这才是有趣的地方 ✨
|
||||
有趣的部分来了。✨
|
||||
|
||||
访问应用的**官方**方式是通过含路径前缀的代理。因此,不出所料,如果没有在 URL 中添加路径前缀,直接访问通过 Uvicorn 运行的 API 文档,不能正常访问,因为需要通过代理才能访问。
|
||||
访问应用的“官方”方式应该是通过我们定义的带有路径前缀的代理。因此,正如预期的那样,如果你尝试不带路径前缀、直接由 Uvicorn 提供的文档界面,它将无法工作,因为它期望通过代理访问。
|
||||
|
||||
输入 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs 查看 API 文档:</a>
|
||||
你可以在 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a> 查看:
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image01.png">
|
||||
|
||||
但输入**官方**链接 `/api/v1/docs`,并使用端口 `9999` 访问 API 文档,就能正常运行了!🎉
|
||||
但如果我们在“官方”URL(代理端口为 `9999`)的 `/api/v1/docs` 访问文档界面,它就能正常工作!🎉
|
||||
|
||||
输入 <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs 查看文档:</a>
|
||||
你可以在 <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> 查看:
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image02.png">
|
||||
|
||||
一切正常。 ✔️
|
||||
完全符合我们的预期。✔️
|
||||
|
||||
这是因为 FastAPI 在 OpenAPI 里使用 `root_path` 提供的 URL 创建默认 `server`。
|
||||
这是因为 FastAPI 使用该 `root_path` 在 OpenAPI 中创建默认的 `server`,其 URL 来自 `root_path`。
|
||||
|
||||
## 附加的服务器
|
||||
## 附加的服务器 { #additional-servers }
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
此用例较难,可以跳过。
|
||||
这是一个更高级的用例,可以跳过。
|
||||
|
||||
///
|
||||
|
||||
默认情况下,**FastAPI** 使用 `root_path` 的链接在 OpenAPI 概图中创建 `server`。
|
||||
默认情况下,**FastAPI** 会在 OpenAPI 模式中使用 `root_path` 的 URL 创建一个 `server`。
|
||||
|
||||
但也可以使用其它备选 `servers`,例如,需要同一个 API 文档与 staging 和生产环境交互。
|
||||
但你也可以提供其他备选的 `servers`,例如你希望让“同一个”文档界面同时与预发布环境和生产环境交互。
|
||||
|
||||
如果传递自定义 `servers` 列表,并有 `root_path`( 因为 API 使用了代理),**FastAPI** 会在列表开头使用这个 `root_path` 插入**服务器**。
|
||||
如果你传入了自定义的 `servers` 列表,并且存在 `root_path`(因为你的 API 位于代理后面),**FastAPI** 会在列表开头插入一个使用该 `root_path` 的“server”。
|
||||
|
||||
例如:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
|
||||
这段代码生产如下 OpenAPI 概图:
|
||||
会生成如下的 OpenAPI 模式:
|
||||
|
||||
```JSON hl_lines="5-7"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
"openapi": "3.1.0",
|
||||
// More stuff here
|
||||
"servers": [
|
||||
{
|
||||
@@ -328,30 +429,38 @@ $ uvicorn main:app --root-path /api/v1
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意,自动生成服务器时,`url` 的值 `/api/v1` 提取自 `roog_path`。
|
||||
注意这个自动生成的服务器,`url` 的值为 `/api/v1`,取自 `root_path`。
|
||||
|
||||
///
|
||||
|
||||
<a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs 的 API 文档所示如下:</a>
|
||||
在 <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> 的文档界面中,它看起来是这样的:
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image03.png">
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
API 文档与所选的服务器进行交互。
|
||||
文档界面会与你所选择的服务器交互。
|
||||
|
||||
///
|
||||
|
||||
### 从 `root_path` 禁用自动服务器
|
||||
/// note | 技术细节
|
||||
|
||||
如果不想让 **FastAPI** 包含使用 `root_path` 的自动服务器,则要使用参数 `root_path_in_servers=False`:
|
||||
OpenAPI 规范中的 `servers` 属性是可选的。
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
|
||||
如果你没有指定 `servers` 参数,并且 `root_path` 等于 `/`,则默认情况下,生成的 OpenAPI 模式中会完全省略 `servers` 属性,这等价于只有一个 `url` 值为 `/` 的服务器。
|
||||
|
||||
这样,就不会在 OpenAPI 概图中包含服务器了。
|
||||
///
|
||||
|
||||
## 挂载子应用
|
||||
### 从 `root_path` 禁用自动服务器 { #disable-automatic-server-from-root-path }
|
||||
|
||||
如需挂载子应用(详见 [子应用 - 挂载](sub-applications.md){.internal-link target=_blank}),也要通过 `root_path` 使用代理,这与正常应用一样,别无二致。
|
||||
如果你不希望 **FastAPI** 包含一个使用 `root_path` 的自动服务器,可以使用参数 `root_path_in_servers=False`:
|
||||
|
||||
FastAPI 在内部使用 `root_path`,因此子应用也可以正常运行。✨
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
|
||||
这样它就不会被包含到 OpenAPI 模式中。
|
||||
|
||||
## 挂载子应用 { #mounting-a-sub-application }
|
||||
|
||||
如果你需要在使用带有 `root_path` 的代理时挂载一个子应用(参见 [子应用 - 挂载](sub-applications.md){.internal-link target=_blank}),你可以像预期的那样正常操作。
|
||||
|
||||
FastAPI 会在内部智能地使用 `root_path`,因此它可以直接正常工作。✨
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
# 自定义响应 - HTML,流,文件和其他
|
||||
# 自定义响应 - HTML、流、文件等 { #custom-response-html-stream-file-others }
|
||||
|
||||
**FastAPI** 默认会使用 `JSONResponse` 返回响应。
|
||||
|
||||
你可以通过直接返回 `Response` 来重载它,参见 [直接返回响应](response-directly.md){.internal-link target=_blank}。
|
||||
|
||||
但如果你直接返回 `Response`,返回数据不会自动转换,也不会自动生成文档(例如,在 HTTP 头 `Content-Type` 中包含特定的「媒体类型」作为生成的 OpenAPI 的一部分)。
|
||||
但如果你直接返回一个 `Response`(或其任意子类,比如 `JSONResponse`),返回数据不会自动转换(即使你声明了 `response_model`),也不会自动生成文档(例如,在生成的 OpenAPI 中,HTTP 头 `Content-Type` 里的特定「媒体类型」不会被包含)。
|
||||
|
||||
你还可以在 *路径操作装饰器* 中声明你想用的 `Response`。
|
||||
你还可以在 *路径操作装饰器* 中通过 `response_class` 参数声明要使用的 `Response`(例如任意 `Response` 子类)。
|
||||
|
||||
你从 *路径操作函数* 中返回的内容将被放在该 `Response` 中。
|
||||
|
||||
并且如果该 `Response` 有一个 JSON 媒体类型(`application/json`),比如使用 `JSONResponse` 或者 `UJSONResponse` 的时候,返回的数据将使用你在路径操作装饰器中声明的任何 Pydantic 的 `response_model` 自动转换(和过滤)。
|
||||
并且如果该 `Response` 有一个 JSON 媒体类型(`application/json`),比如使用 `JSONResponse` 或 `UJSONResponse` 的时候,返回的数据将使用你在路径操作装饰器中声明的任何 Pydantic 的 `response_model` 自动转换(和过滤)。
|
||||
|
||||
/// note | 说明
|
||||
/// note | 注意
|
||||
|
||||
如果你使用不带有任何媒体类型的响应类,FastAPI 认为你的响应没有任何内容,所以不会在生成的OpenAPI文档中记录响应格式。
|
||||
如果你使用不带有任何媒体类型的响应类,FastAPI 会认为你的响应没有任何内容,所以不会在生成的 OpenAPI 文档中记录响应格式。
|
||||
|
||||
///
|
||||
|
||||
## 使用 `ORJSONResponse`
|
||||
## 使用 `ORJSONResponse` { #use-orjsonresponse }
|
||||
|
||||
例如,如果你需要压榨性能,你可以安装并使用 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> 并将响应设置为 `ORJSONResponse`。
|
||||
|
||||
导入你想要使用的 `Response` 类(子类)然后在 *路径操作装饰器* 中声明它。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
|
||||
对于较大的响应,直接返回一个 `Response` 会比返回一个字典快得多。
|
||||
|
||||
/// info | 提示
|
||||
这是因为默认情况下,FastAPI 会检查其中的每一项并确保它可以被序列化为 JSON,使用教程中解释的相同 [JSON 兼容编码器](../tutorial/encoder.md){.internal-link target=_blank}。这正是它允许你返回「任意对象」的原因,例如数据库模型。
|
||||
|
||||
但如果你确定你返回的内容是「可以用 JSON 序列化」的,你可以将它直接传给响应类,从而避免在传给响应类之前先通过 `jsonable_encoder` 带来的额外开销。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
|
||||
/// info | 信息
|
||||
|
||||
参数 `response_class` 也会用来定义响应的「媒体类型」。
|
||||
|
||||
@@ -36,22 +42,22 @@
|
||||
|
||||
///
|
||||
|
||||
/// tip | 小贴士
|
||||
/// tip | 提示
|
||||
|
||||
`ORJSONResponse` 目前只在 FastAPI 中可用,而在 Starlette 中不可用。
|
||||
|
||||
///
|
||||
|
||||
## HTML 响应
|
||||
## HTML 响应 { #html-response }
|
||||
|
||||
使用 `HTMLResponse` 来从 **FastAPI** 中直接返回一个 HTML 响应。
|
||||
|
||||
* 导入 `HTMLResponse`。
|
||||
* 将 `HTMLResponse` 作为你的 *路径操作* 的 `response_class` 参数传入。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
|
||||
/// info | 提示
|
||||
/// info | 信息
|
||||
|
||||
参数 `response_class` 也会用来定义响应的「媒体类型」。
|
||||
|
||||
@@ -61,13 +67,13 @@
|
||||
|
||||
///
|
||||
|
||||
### 返回一个 `Response`
|
||||
### 返回一个 `Response` { #return-a-response }
|
||||
|
||||
正如你在 [直接返回响应](response-directly.md){.internal-link target=_blank} 中了解到的,你也可以通过直接返回响应在 *路径操作* 中直接重载响应。
|
||||
|
||||
和上面一样的例子,返回一个 `HTMLResponse` 看起来可能是这样:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
@@ -75,33 +81,33 @@
|
||||
|
||||
///
|
||||
|
||||
/// info | 提示
|
||||
/// info | 信息
|
||||
|
||||
当然,实际的 `Content-Type` 头,状态码等等,将来自于你返回的 `Response` 对象。
|
||||
当然,实际的 `Content-Type` 头、状态码等等,将来自于你返回的 `Response` 对象。
|
||||
|
||||
///
|
||||
|
||||
### OpenAPI 中的文档和重载 `Response`
|
||||
### 在 OpenAPI 中文档化并重载 `Response` { #document-in-openapi-and-override-response }
|
||||
|
||||
如果你想要在函数内重载响应,但是同时在 OpenAPI 中文档化「媒体类型」,你可以使用 `response_class` 参数并返回一个 `Response` 对象。
|
||||
|
||||
接着 `response_class` 参数只会被用来文档化 OpenAPI 的 *路径操作*,你的 `Response` 用来返回响应。
|
||||
|
||||
### 直接返回 `HTMLResponse`
|
||||
#### 直接返回 `HTMLResponse` { #return-an-htmlresponse-directly }
|
||||
|
||||
比如像这样:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004.py hl[7,23,21] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
|
||||
在这个例子中,函数 `generate_html_response()` 已经生成并返回 `Response` 对象而不是在 `str` 中返回 HTML。
|
||||
|
||||
通过返回函数 `generate_html_response()` 的调用结果,你已经返回一个重载 **FastAPI** 默认行为的 `Response` 对象,
|
||||
通过返回函数 `generate_html_response()` 的调用结果,你已经返回一个重载 **FastAPI** 默认行为的 `Response` 对象。
|
||||
|
||||
但如果你在 `response_class` 中也传入了 `HTMLResponse`,**FastAPI** 会知道如何在 OpenAPI 和交互式文档中使用 `text/html` 将其文档化为 HTML。
|
||||
但如果你在 `response_class` 中也传入了 `HTMLResponse`,**FastAPI** 会知道如何在 OpenAPI 和交互式文档中使用 `text/html` 将其文档化为 HTML:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## 可用响应
|
||||
## 可用响应 { #available-responses }
|
||||
|
||||
这里有一些可用的响应。
|
||||
|
||||
@@ -115,7 +121,7 @@
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
### `Response` { #response }
|
||||
|
||||
其他全部的响应都继承自主类 `Response`。
|
||||
|
||||
@@ -128,77 +134,115 @@
|
||||
* `headers` - 一个由字符串组成的 `dict`。
|
||||
* `media_type` - 一个给出媒体类型的 `str`,比如 `"text/html"`。
|
||||
|
||||
FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 media_type 的 Content-Type 头,并为文本类型附加一个字符集。
|
||||
FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它还将包含一个基于 `media_type` 的 Content-Type 头,并为文本类型附加一个字符集。
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse`
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
如上文所述,接受文本或字节并返回 HTML 响应。
|
||||
|
||||
### `PlainTextResponse`
|
||||
### `PlainTextResponse` { #plaintextresponse }
|
||||
|
||||
接受文本或字节并返回纯文本响应。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse`
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
接受数据并返回一个 `application/json` 编码的响应。
|
||||
|
||||
如上文所述,这是 **FastAPI** 中使用的默认响应。
|
||||
|
||||
### `ORJSONResponse`
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
如上文所述,`ORJSONResponse` 是一个使用 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> 的快速的可选 JSON 响应。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
### `UJSONResponse`
|
||||
这需要先安装 `orjson`,例如使用 `pip install orjson`。
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
`UJSONResponse` 是一个使用 <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a> 的可选 JSON 响应。
|
||||
|
||||
/// info | 信息
|
||||
|
||||
这需要先安装 `ujson`,例如使用 `pip install ujson`。
|
||||
|
||||
///
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
在处理某些边缘情况时,`ujson` 不如 Python 的内置实现那么谨慎。
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
|
||||
/// tip | 小贴士
|
||||
/// tip | 提示
|
||||
|
||||
`ORJSONResponse` 可能是一个更快的选择。
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
返回 HTTP 重定向。默认情况下使用 307 状态代码(临时重定向)。
|
||||
返回 HTTP 重定向。默认情况下使用 307 状态码(临时重定向)。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
|
||||
你可以直接返回一个 `RedirectResponse`:
|
||||
|
||||
### `StreamingResponse`
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
或者你可以把它用于 `response_class` 参数:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
|
||||
如果你这么做,那么你可以在 *路径操作* 函数中直接返回 URL。
|
||||
|
||||
在这种情况下,将使用 `RedirectResponse` 的默认 `status_code`,即 `307`。
|
||||
|
||||
---
|
||||
|
||||
你也可以将 `status_code` 参数和 `response_class` 参数结合使用:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
采用异步生成器或普通生成器/迭代器,然后流式传输响应主体。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
|
||||
#### 对类似文件的对象使用 `StreamingResponse`
|
||||
#### 对类似文件的对象使用 `StreamingResponse` { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
如果您有类似文件的对象(例如,由 `open()` 返回的对象),则可以在 `StreamingResponse` 中将其返回。
|
||||
如果您有一个<a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">类文件</a>对象(例如由 `open()` 返回的对象),你可以创建一个生成器函数来迭代该类文件对象。
|
||||
|
||||
包括许多与云存储,视频处理等交互的库。
|
||||
这样,你就不必先把它全部读入内存,可以将该生成器函数传给 `StreamingResponse` 并返回它。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
|
||||
这也包括许多与云存储、视频处理等交互的库。
|
||||
|
||||
/// tip | 小贴士
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
|
||||
1. 这是生成器函数。之所以是「生成器函数」,是因为它内部包含 `yield` 语句。
|
||||
2. 通过使用 `with` 代码块,我们可以确保在生成器函数完成后关闭类文件对象。因此,在它完成发送响应之后会被关闭。
|
||||
3. 这个 `yield from` 告诉函数去迭代名为 `file_like` 的那个对象。然后,对于每个被迭代出来的部分,都把该部分作为来自这个生成器函数(`iterfile`)的值再 `yield` 出去。
|
||||
|
||||
因此,它是一个把「生成」工作内部转交给其他东西的生成器函数。
|
||||
|
||||
通过这种方式,我们可以把它放在 `with` 代码块中,从而确保类文件对象在结束后被关闭。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意在这里,因为我们使用的是不支持 `async` 和 `await` 的标准 `open()`,我们使用普通的 `def` 声明了路径操作。
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
### `FileResponse` { #fileresponse }
|
||||
|
||||
异步传输文件作为响应。
|
||||
|
||||
@@ -209,10 +253,60 @@ FastAPI(实际上是 Starlette)将自动包含 Content-Length 的头。它
|
||||
* `media_type` - 给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。
|
||||
* `filename` - 如果给出,它将包含在响应的 `Content-Disposition` 中。
|
||||
|
||||
文件响应将包含适当的 `Content-Length`,`Last-Modified` 和 `ETag` 的响应头。
|
||||
文件响应将包含适当的 `Content-Length`、`Last-Modified` 和 `ETag` 响应头。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
|
||||
## 额外文档
|
||||
你也可以使用 `response_class` 参数:
|
||||
|
||||
您还可以使用 `response` 在 OpenAPI 中声明媒体类型和许多其他详细信息:[OpenAPI 中的额外文档](additional-responses.md){.internal-link target=_blank}。
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
|
||||
在这种情况下,你可以在 *路径操作* 函数中直接返回文件路径。
|
||||
|
||||
## 自定义响应类 { #custom-response-class }
|
||||
|
||||
你可以创建你自己的自定义响应类,继承自 `Response` 并使用它。
|
||||
|
||||
例如,假设你想使用 <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>,但要使用内置 `ORJSONResponse` 类没有启用的一些自定义设置。
|
||||
|
||||
假设你想让它返回带缩进、格式化的 JSON,因此你想使用 orjson 选项 `orjson.OPT_INDENT_2`。
|
||||
|
||||
你可以创建一个 `CustomORJSONResponse`。你需要做的主要事情是实现一个返回 `bytes` 的 `Response.render(content)` 方法:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
|
||||
现在,不再是返回:
|
||||
|
||||
```json
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
…这个响应将返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
当然,你很可能会找到比格式化 JSON 更好的方式来利用这一点。😉
|
||||
|
||||
## 默认响应类 { #default-response-class }
|
||||
|
||||
在创建 **FastAPI** 类实例或 `APIRouter` 时,你可以指定默认要使用的响应类。
|
||||
|
||||
用于定义它的参数是 `default_response_class`。
|
||||
|
||||
在下面的示例中,**FastAPI** 会在所有 *路径操作* 中默认使用 `ORJSONResponse`,而不是 `JSONResponse`。
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
你仍然可以像之前一样在 *路径操作* 中重载 `response_class`。
|
||||
|
||||
///
|
||||
|
||||
## 额外文档 { #additional-documentation }
|
||||
|
||||
你还可以使用 `responses` 在 OpenAPI 中声明媒体类型和许多其他详细信息:[OpenAPI 中的额外文档](additional-responses.md){.internal-link target=_blank}。
|
||||
|
||||
@@ -1,97 +1,87 @@
|
||||
# 使用数据类
|
||||
# 使用数据类 { #using-dataclasses }
|
||||
|
||||
FastAPI 基于 **Pydantic** 构建,前文已经介绍过如何使用 Pydantic 模型声明请求与响应。
|
||||
FastAPI 基于 **Pydantic** 构建,我已经向你展示过如何使用 Pydantic 模型声明请求与响应。
|
||||
|
||||
但 FastAPI 还可以使用数据类(<a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>):
|
||||
但 FastAPI 也支持以相同方式使用 <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a>:
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial001.py hl[1,7:12,19:20] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
|
||||
|
||||
这还是借助于 **Pydantic** 及其<a href="https://pydantic-docs.helpmanual.io/usage/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">内置的 `dataclasses`</a>。
|
||||
这仍然得益于 **Pydantic**,因为它对 <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">`dataclasses` 的内置支持</a>。
|
||||
|
||||
因此,即便上述代码没有显式使用 Pydantic,FastAPI 仍会使用 Pydantic 把标准数据类转换为 Pydantic 数据类(`dataclasses`)。
|
||||
因此,即便上面的代码没有显式使用 Pydantic,FastAPI 也会使用 Pydantic 将那些标准数据类转换为 Pydantic 风格的 dataclasses。
|
||||
|
||||
并且,它仍然支持以下功能:
|
||||
|
||||
* 数据验证
|
||||
* 数据序列化
|
||||
* 数据存档等
|
||||
* 数据文档等
|
||||
|
||||
数据类的和运作方式与 Pydantic 模型相同。实际上,它的底层使用的也是 Pydantic。
|
||||
这与使用 Pydantic 模型时的工作方式相同。而且底层实际上也是借助 Pydantic 实现的。
|
||||
|
||||
/// info | 说明
|
||||
/// info | 信息
|
||||
|
||||
注意,数据类不支持 Pydantic 模型的所有功能。
|
||||
请注意,数据类不能完成 Pydantic 模型能做的所有事情。
|
||||
|
||||
因此,开发时仍需要使用 Pydantic 模型。
|
||||
因此,你可能仍然需要使用 Pydantic 模型。
|
||||
|
||||
但如果数据类很多,这一技巧能给 FastAPI 开发 Web API 增添不少助力。🤓
|
||||
但如果你已有一堆数据类,这个技巧可以让它们很好地为使用 FastAPI 的 Web API 所用。🤓
|
||||
|
||||
///
|
||||
|
||||
## `response_model` 使用数据类
|
||||
## 在 `response_model` 中使用数据类 { #dataclasses-in-response-model }
|
||||
|
||||
在 `response_model` 参数中使用 `dataclasses`:
|
||||
你也可以在 `response_model` 参数中使用 `dataclasses`:
|
||||
|
||||
{* ../../docs_src/dataclasses_/tutorial002.py hl[1,7:13,19] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
|
||||
|
||||
本例把数据类自动转换为 Pydantic 数据类。
|
||||
该数据类会被自动转换为 Pydantic 的数据类。
|
||||
|
||||
API 文档中也会显示相关概图:
|
||||
这样,它的模式会显示在 API 文档界面中:
|
||||
|
||||
<img src="/img/tutorial/dataclasses/image01.png">
|
||||
|
||||
## 在嵌套数据结构中使用数据类
|
||||
## 在嵌套数据结构中使用数据类 { #dataclasses-in-nested-data-structures }
|
||||
|
||||
您还可以把 `dataclasses` 与其它类型注解组合在一起,创建嵌套数据结构。
|
||||
你也可以把 `dataclasses` 与其它类型注解组合在一起,创建嵌套数据结构。
|
||||
|
||||
还有一些情况也可以使用 Pydantic 的 `dataclasses`。例如,在 API 文档中显示错误。
|
||||
在某些情况下,你可能仍然需要使用 Pydantic 的 `dataclasses` 版本。例如,如果自动生成的 API 文档出现错误。
|
||||
|
||||
本例把标准的 `dataclasses` 直接替换为 `pydantic.dataclasses`:
|
||||
在这种情况下,你可以直接把标准的 `dataclasses` 替换为 `pydantic.dataclasses`,它是一个可直接替换的实现:
|
||||
|
||||
```{ .python .annotate hl_lines="1 5 8-11 14-17 23-25 28" }
|
||||
{!../../docs_src/dataclasses_/tutorial003.py!}
|
||||
```
|
||||
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
|
||||
|
||||
1. 本例依然要从标准的 `dataclasses` 中导入 `field`;
|
||||
1. 我们仍然从标准库的 `dataclasses` 导入 `field`。
|
||||
2. `pydantic.dataclasses` 是 `dataclasses` 的可直接替换版本。
|
||||
3. `Author` 数据类包含一个由 `Item` 数据类组成的列表。
|
||||
4. `Author` 数据类被用作 `response_model` 参数。
|
||||
5. 你可以将其它标准类型注解与数据类一起用作请求体。
|
||||
|
||||
2. 使用 `pydantic.dataclasses` 直接替换 `dataclasses`;
|
||||
在本例中,它是一个 `Item` 数据类列表。
|
||||
6. 这里我们返回一个字典,里面的 `items` 是一个数据类列表。
|
||||
|
||||
3. `Author` 数据类包含 `Item` 数据类列表;
|
||||
FastAPI 仍然能够将数据<abbr title="把数据转换为可以传输的格式">序列化</abbr>为 JSON。
|
||||
7. 这里的 `response_model` 使用了 “`Author` 数据类列表” 的类型注解。
|
||||
|
||||
4. `Author` 数据类用于 `response_model` 参数;
|
||||
同样,你可以将 `dataclasses` 与标准类型注解组合使用。
|
||||
8. 注意,这个 *路径操作函数* 使用的是常规的 `def` 而不是 `async def`。
|
||||
|
||||
5. 其它带有数据类的标准类型注解也可以作为请求体;
|
||||
一如既往,在 FastAPI 中你可以按需组合 `def` 和 `async def`。
|
||||
|
||||
本例使用的是 `Item` 数据类列表;
|
||||
如果需要回顾何时用哪一个,请查看关于 [`async` 和 `await`](../async.md#in-a-hurry){.internal-link target=_blank} 的文档中的 _“急不可待?”_ 一节。
|
||||
9. 这个 *路径操作函数* 返回的不是数据类(当然也可以返回数据类),而是包含内部数据的字典列表。
|
||||
|
||||
6. 这行代码返回的是包含 `items` 的字典,`items` 是数据类列表;
|
||||
FastAPI 会使用(包含数据类的)`response_model` 参数来转换响应。
|
||||
|
||||
FastAPI 仍能把数据<abbr title="把数据转换为可以传输的格式">序列化</abbr>为 JSON;
|
||||
你可以将 `dataclasses` 与其它类型注解以多种不同方式组合,来构建复杂的数据结构。
|
||||
|
||||
7. 这行代码中,`response_model` 的类型注解是 `Author` 数据类列表;
|
||||
更多细节请参考上面代码中的内联注释提示。
|
||||
|
||||
再一次,可以把 `dataclasses` 与标准类型注解一起使用;
|
||||
## 深入学习 { #learn-more }
|
||||
|
||||
8. 注意,*路径操作函数*使用的是普通函数,不是异步函数;
|
||||
你还可以把 `dataclasses` 与其它 Pydantic 模型组合、从它们继承、把它们包含到你自己的模型中等。
|
||||
|
||||
与往常一样,在 FastAPI 中,可以按需组合普通函数与异步函数;
|
||||
想了解更多,请查看 <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">Pydantic 关于 dataclasses 的文档</a>。
|
||||
|
||||
如果不清楚何时使用异步函数或普通函数,请参阅**急不可待?**一节中对 <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank" class="internal-link">`async` 与 `await`</a> 的说明;
|
||||
## 版本 { #version }
|
||||
|
||||
9. *路径操作函数*返回的不是数据类(虽然它可以返回数据类),而是返回内含数据的字典列表;
|
||||
|
||||
FastAPI 使用(包含数据类的) `response_model` 参数转换响应。
|
||||
|
||||
把 `dataclasses` 与其它类型注解组合在一起,可以组成不同形式的复杂数据结构。
|
||||
|
||||
更多内容详见上述代码内的注释。
|
||||
|
||||
## 深入学习
|
||||
|
||||
您还可以把 `dataclasses` 与其它 Pydantic 模型组合在一起,继承合并的模型,把它们包含在您自己的模型里。
|
||||
|
||||
详见 <a href="https://pydantic-docs.helpmanual.io/usage/dataclasses/" class="external-link" target="_blank">Pydantic 官档 - 数据类</a>。
|
||||
|
||||
## 版本
|
||||
|
||||
本章内容自 FastAPI `0.67.0` 版起生效。🔖
|
||||
自 FastAPI 版本 `0.67.0` 起可用。🔖
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# 生命周期事件
|
||||
# 生命周期事件 { #lifespan-events }
|
||||
|
||||
你可以定义在应用**启动**前执行的逻辑(代码)。这意味着在应用**开始接收请求**之前,这些代码只会被执行**一次**。
|
||||
|
||||
同样地,你可以定义在应用**关闭**时应执行的逻辑。在这种情况下,这段代码将在**处理可能的多次请求后**执行**一次**。
|
||||
|
||||
因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**("生命周期"这个词很重要😉)。
|
||||
因为这段代码在应用开始接收请求**之前**执行,也会在处理可能的若干请求**之后**执行,它覆盖了整个应用程序的**生命周期**(“生命周期”这个词很重要😉)。
|
||||
|
||||
这对于设置你需要在整个应用中使用的**资源**非常有用,这些资源在请求之间**共享**,你可能需要在之后进行**释放**。例如,数据库连接池,或加载一个共享的机器学习模型。
|
||||
|
||||
## 用例
|
||||
## 用例 { #use-case }
|
||||
|
||||
让我们从一个示例用例开始,看看如何解决它。
|
||||
让我们从一个示例**用例**开始,看看如何用它来解决问题。
|
||||
|
||||
假设你有几个**机器学习的模型**,你想要用它们来处理请求。
|
||||
假设你有几个**机器学习的模型**,你想要用它们来处理请求。🤖
|
||||
|
||||
相同的模型在请求之间是共享的,因此并非每个请求或每个用户各自拥有一个模型。
|
||||
|
||||
@@ -20,19 +20,17 @@
|
||||
|
||||
你可以在模块/文件的顶部加载它,但这也意味着即使你只是在运行一个简单的自动化测试,它也会**加载模型**,这样测试将**变慢**,因为它必须在能够独立运行代码的其他部分之前等待模型加载完成。
|
||||
|
||||
这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是代码执行时。
|
||||
这就是我们要解决的问题——在处理请求前加载模型,但只是在应用开始接收请求前,而不是在代码被加载时。
|
||||
|
||||
## 生命周期 lifespan
|
||||
## Lifespan { #lifespan }
|
||||
|
||||
你可以使用`FastAPI()`应用的`lifespan`参数和一个上下文管理器(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。
|
||||
你可以使用 `FastAPI` 应用的 `lifespan` 参数和一个“上下文管理器”(稍后我将为你展示)来定义**启动**和**关闭**的逻辑。
|
||||
|
||||
让我们从一个例子开始,然后详细介绍。
|
||||
|
||||
我们使用`yield`创建了一个异步函数`lifespan()`像这样:
|
||||
我们使用 `yield` 创建了一个异步函数 `lifespan()` 像这样:
|
||||
|
||||
```Python hl_lines="16 19"
|
||||
{!../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
|
||||
在这里,我们在 `yield` 之前将(虚拟的)模型函数放入机器学习模型的字典中,以此模拟加载模型的耗时**启动**操作。这段代码将在应用程序**开始处理请求之前**执行,即**启动**期间。
|
||||
|
||||
@@ -40,35 +38,31 @@
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
**关闭**事件只会在你停止应用时触发。
|
||||
**关闭**事件会在你**停止**应用时发生。
|
||||
|
||||
可能你需要启动一个新版本,或者你只是你厌倦了运行它。 🤷
|
||||
可能你需要启动一个新版本,或者你只是厌倦了运行它。 🤷
|
||||
|
||||
///
|
||||
|
||||
## 生命周期函数
|
||||
### 生命周期函数 { #lifespan-function }
|
||||
|
||||
首先要注意的是,我们定义了一个带有 `yield` 的异步函数。这与带有 `yield` 的依赖项非常相似。
|
||||
|
||||
```Python hl_lines="14-19"
|
||||
{!../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
|
||||
这个函数在 `yield`之前的部分,会在应用启动前执行。
|
||||
这个函数在 `yield` 之前的部分,会在应用启动前执行。
|
||||
|
||||
剩下的部分在 `yield` 之后,会在应用完成后执行。
|
||||
|
||||
## 异步上下文管理器
|
||||
### 异步上下文管理器 { #async-context-manager }
|
||||
|
||||
如你所见,这个函数有一个装饰器 `@asynccontextmanager` 。
|
||||
如你所见,这个函数有一个装饰器 `@asynccontextmanager`。
|
||||
|
||||
它将函数转化为所谓的“**异步上下文管理器**”。
|
||||
|
||||
```Python hl_lines="1 13"
|
||||
{!../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
|
||||
在 Python 中, **上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
|
||||
在 Python 中,**上下文管理器**是一个你可以在 `with` 语句中使用的东西,例如,`open()` 可以作为上下文管理器使用。
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
@@ -82,21 +76,19 @@ async with lifespan(app):
|
||||
await do_stuff()
|
||||
```
|
||||
|
||||
你可以像上面一样创建了一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。
|
||||
你可以像上面一样创建一个上下文管理器或者异步上下文管理器,它的作用是在进入 `with` 块时,执行 `yield` 之前的代码,并且在离开 `with` 块时,执行 `yield` 后面的代码。
|
||||
|
||||
但在我们上面的例子里,我们并不是直接使用,而是传递给 FastAPI 来供其使用。
|
||||
|
||||
`FastAPI()` 的 `lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的上下文管理器 `lifespan` 传给它。
|
||||
`FastAPI` 的 `lifespan` 参数接受一个**异步上下文管理器**,所以我们可以把我们新定义的异步上下文管理器 `lifespan` 传给它。
|
||||
|
||||
```Python hl_lines="22"
|
||||
{!../../docs_src/events/tutorial003.py!}
|
||||
```
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
|
||||
## 替代事件(弃用)
|
||||
## 替代事件(弃用) { #alternative-events-deprecated }
|
||||
|
||||
/// warning | 警告
|
||||
|
||||
配置**启动**和**关闭**事件的推荐方法是使用 `FastAPI()` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。
|
||||
配置**启动**和**关闭**的推荐方法是使用 `FastAPI` 应用的 `lifespan` 参数,如前所示。如果你提供了一个 `lifespan` 参数,启动(`startup`)和关闭(`shutdown`)事件处理器将不再生效。要么使用 `lifespan`,要么配置所有事件,两者不能共用。
|
||||
|
||||
你可以跳过这一部分。
|
||||
|
||||
@@ -104,70 +96,70 @@ async with lifespan(app):
|
||||
|
||||
有一种替代方法可以定义在**启动**和**关闭**期间执行的逻辑。
|
||||
|
||||
**FastAPI** 支持定义在应用启动前,或应用关闭时执行的事件处理器(函数)。
|
||||
你可以定义在应用启动前或应用关闭时需要执行的事件处理器(函数)。
|
||||
|
||||
事件函数既可以声明为异步函数(`async def`),也可以声明为普通函数(`def`)。
|
||||
|
||||
### `startup` 事件
|
||||
### `startup` 事件 { #startup-event }
|
||||
|
||||
使用 `startup` 事件声明 `app` 启动前运行的函数:
|
||||
使用事件 `"startup"` 声明一个在应用启动前运行的函数:
|
||||
|
||||
{* ../../docs_src/events/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
|
||||
本例中,`startup` 事件处理器函数为项目数据库(只是**字典**)提供了一些初始值。
|
||||
本例中,`startup` 事件处理器函数为项目“数据库”(只是一个 `dict`)提供了一些初始值。
|
||||
|
||||
**FastAPI** 支持多个事件处理器函数。
|
||||
|
||||
只有所有 `startup` 事件处理器运行完毕,**FastAPI** 应用才开始接收请求。
|
||||
|
||||
### `shutdown` 事件
|
||||
### `shutdown` 事件 { #shutdown-event }
|
||||
|
||||
使用 `shutdown` 事件声明 `app` 关闭时运行的函数:
|
||||
使用事件 `"shutdown"` 声明一个在应用关闭时运行的函数:
|
||||
|
||||
{* ../../docs_src/events/tutorial002.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
|
||||
此处,`shutdown` 事件处理器函数在 `log.txt` 中写入一行文本 `Application shutdown`。
|
||||
此处,`shutdown` 事件处理器函数会向文件 `log.txt` 写入一行文本 `"Application shutdown"`。
|
||||
|
||||
/// info | 说明
|
||||
/// info | 信息
|
||||
|
||||
`open()` 函数中,`mode="a"` 指的是**追加**。因此这行文本会添加在文件已有内容之后,不会覆盖之前的内容。
|
||||
在 `open()` 函数中,`mode="a"` 指的是“追加”。因此这行文本会添加在文件已有内容之后,不会覆盖之前的内容。
|
||||
|
||||
///
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意,本例使用 Python `open()` 标准函数与文件交互。
|
||||
注意,本例使用 Python 标准的 `open()` 函数与文件交互。
|
||||
|
||||
这个函数执行 I/O(输入/输出)操作,需要等待内容写进磁盘。
|
||||
这个函数执行 I/O(输入/输出)操作,需要“等待”内容写进磁盘。
|
||||
|
||||
但 `open()` 函数不支持使用 `async` 与 `await`。
|
||||
但 `open()` 不使用 `async` 和 `await`。
|
||||
|
||||
因此,声明事件处理函数要使用 `def`,不能使用 `asnyc def`。
|
||||
因此,声明事件处理函数要使用 `def`,而不是 `async def`。
|
||||
|
||||
///
|
||||
|
||||
### `startup` 和 `shutdown` 一起使用
|
||||
### `startup` 和 `shutdown` 一起使用 { #startup-and-shutdown-together }
|
||||
|
||||
启动和关闭的逻辑很可能是连接在一起的,你可能希望启动某个东西然后结束它,获取一个资源然后释放它等等。
|
||||
|
||||
在不共享逻辑或变量的不同函数中处理这些逻辑比较困难,因为你需要在全局变量中存储值或使用类似的方式。
|
||||
|
||||
因此,推荐使用 `lifespan` 。
|
||||
因此,推荐使用上面所述的 `lifespan`。
|
||||
|
||||
## 技术细节
|
||||
## 技术细节 { #technical-details }
|
||||
|
||||
只是为好奇者提供的技术细节。🤓
|
||||
|
||||
在底层,这部分是<a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">生命周期协议</a>的一部分,参见 ASGI 技术规范,定义了称为启动(`startup`)和关闭(`shutdown`)的事件。
|
||||
在底层,这部分是 ASGI 技术规范中的 <a href="https://asgi.readthedocs.io/en/latest/specs/lifespan.html" class="external-link" target="_blank">Lifespan 协议</a>的一部分,定义了称为 `startup` 和 `shutdown` 的事件。
|
||||
|
||||
/// info | 说明
|
||||
/// info | 信息
|
||||
|
||||
有关事件处理器的详情,请参阅 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette 官档 - 事件</a>。
|
||||
你可以在 <a href="https://www.starlette.dev/lifespan/" class="external-link" target="_blank">Starlette 的 Lifespan 文档</a> 中阅读更多关于 `lifespan` 处理器的内容。
|
||||
|
||||
包括如何处理生命周期状态,这可以用于程序的其他部分。
|
||||
包括如何处理生命周期状态,以便在代码的其他部分使用。
|
||||
|
||||
///
|
||||
|
||||
## 子应用
|
||||
## 子应用 { #sub-applications }
|
||||
|
||||
🚨 **FastAPI** 只会触发主应用中的生命周期事件,不包括[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}中的。
|
||||
🚨 请注意,这些生命周期事件(startup 和 shutdown)只会在主应用上执行,不会在[子应用 - 挂载](sub-applications.md){.internal-link target=_blank}上执行。
|
||||
|
||||
@@ -1,237 +1,208 @@
|
||||
# 生成客户端
|
||||
# 生成 SDK { #generating-sdks }
|
||||
|
||||
因为 **FastAPI** 是基于OpenAPI规范的,自然您可以使用许多相匹配的工具,包括自动生成API文档 (由 Swagger UI 提供)。
|
||||
因为 **FastAPI** 基于 **OpenAPI** 规范,它的 API 可以用许多工具都能理解的标准格式来描述。
|
||||
|
||||
一个不太明显而又特别的优势是,你可以为你的API针对不同的**编程语言**来**生成客户端**(有时候被叫做 <abbr title="Software Development Kits">**SDKs**</abbr> )。
|
||||
这让你可以轻松生成最新的**文档**、多语言的客户端库(<abbr title="Software Development Kits - 软件开发工具包">**SDKs**</abbr>),以及与代码保持同步的**测试**或**自动化工作流**。
|
||||
|
||||
## OpenAPI 客户端生成
|
||||
本指南将带你为 FastAPI 后端生成一个 **TypeScript SDK**。
|
||||
|
||||
有许多工具可以从**OpenAPI**生成客户端。
|
||||
## 开源 SDK 生成器 { #open-source-sdk-generators }
|
||||
|
||||
一个常见的工具是 <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>。
|
||||
一个功能多样的选择是 <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>,它支持**多种编程语言**,可以根据你的 OpenAPI 规范生成 SDK。
|
||||
|
||||
如果您正在开发**前端**,一个非常有趣的替代方案是 <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>。
|
||||
对于 **TypeScript 客户端**,<a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> 是为 TypeScript 生态打造的专用方案,提供优化的使用体验。
|
||||
|
||||
## 生成一个 TypeScript 前端客户端
|
||||
你还可以在 <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a> 上发现更多 SDK 生成器。
|
||||
|
||||
让我们从一个简单的 FastAPI 应用开始:
|
||||
/// tip | 提示
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
请注意,*路径操作* 定义了他们所用于请求数据和回应数据的模型,所使用的模型是`Item` 和 `ResponseMessage`。
|
||||
|
||||
### API 文档
|
||||
|
||||
如果您访问API文档,您将看到它具有在请求中发送和在响应中接收数据的**模式(schemas)**:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
您可以看到这些模式,因为它们是用程序中的模型声明的。
|
||||
|
||||
那些信息可以在应用的 **OpenAPI模式** 被找到,然后显示在API文档中(通过Swagger UI)。
|
||||
|
||||
OpenAPI中所包含的模型里有相同的信息可以用于 **生成客户端代码**。
|
||||
|
||||
### 生成一个TypeScript 客户端
|
||||
|
||||
现在我们有了带有模型的应用,我们可以为前端生成客户端代码。
|
||||
|
||||
#### 安装 `openapi-ts`
|
||||
|
||||
您可以使用以下工具在前端代码中安装 `openapi-ts`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
#### 生成客户端代码
|
||||
|
||||
要生成客户端代码,您可以使用现在将要安装的命令行应用程序 `openapi-ts`。
|
||||
|
||||
因为它安装在本地项目中,所以您可能无法直接使用此命令,但您可以将其放在 `package.json` 文件中。
|
||||
|
||||
它可能看起来是这样的:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在这里添加 NPM `generate-client` 脚本后,您可以使用以下命令运行它:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
此命令将在 `./src/client` 中生成代码,并将在其内部使用 `axios`(前端HTTP库)。
|
||||
|
||||
### 尝试客户端代码
|
||||
|
||||
现在您可以导入并使用客户端代码,它可能看起来像这样,请注意,您可以为这些方法使用自动补全:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
您还将自动补全要发送的数据:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image03.png">
|
||||
|
||||
/// tip
|
||||
|
||||
请注意, `name` 和 `price` 的自动补全,是通过其在`Item`模型(FastAPI)中的定义实现的。
|
||||
FastAPI 会自动生成 **OpenAPI 3.1** 规范,因此你使用的任何工具都必须支持该版本。
|
||||
|
||||
///
|
||||
|
||||
如果发送的数据字段不符,你也会看到编辑器的错误提示:
|
||||
## 来自 FastAPI 赞助商的 SDK 生成器 { #sdk-generators-from-fastapi-sponsors }
|
||||
|
||||
本节介绍的是由赞助 FastAPI 的公司提供的、具备**风险投资背景**或**公司支持**的方案。这些产品在高质量生成的 SDK 之上,提供了**更多特性**和**集成**。
|
||||
|
||||
通过 ✨ [**赞助 FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨,这些公司帮助确保框架及其**生态**保持健康并且**可持续**。
|
||||
|
||||
他们的赞助也体现了对 FastAPI **社区**(也就是你)的高度承诺,不仅关注提供**优秀的服务**,也支持一个**健壮且繁荣的框架**——FastAPI。🙇
|
||||
|
||||
例如,你可以尝试:
|
||||
|
||||
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
|
||||
* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
|
||||
其中一些方案也可能是开源的或提供免费层级,你可以不花钱就先试用。其他商业 SDK 生成器也可在网上找到。🤓
|
||||
|
||||
## 创建一个 TypeScript SDK { #create-a-typescript-sdk }
|
||||
|
||||
先从一个简单的 FastAPI 应用开始:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
请注意,这些*路径操作*使用 `Item` 和 `ResponseMessage` 模型来定义它们的请求载荷和响应载荷。
|
||||
|
||||
### API 文档 { #api-docs }
|
||||
|
||||
访问 `/docs` 时,你会看到有用于请求发送和响应接收数据的**模式**:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
之所以能看到这些模式,是因为它们在应用中用模型声明了。
|
||||
|
||||
这些信息会包含在应用的 **OpenAPI 模式** 中,并显示在 API 文档里。
|
||||
|
||||
OpenAPI 中包含的这些模型信息就是用于**生成客户端代码**的基础。
|
||||
|
||||
### Hey API { #hey-api }
|
||||
|
||||
当我们有了带模型的 FastAPI 应用后,可以使用 Hey API 来生成 TypeScript 客户端。最快的方式是通过 npx:
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
```
|
||||
|
||||
这会在 `./src/client` 生成一个 TypeScript SDK。
|
||||
|
||||
你可以在其官网了解如何<a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">安装 `@hey-api/openapi-ts`</a>,以及阅读<a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">生成结果</a>的说明。
|
||||
|
||||
### 使用 SDK { #using-the-sdk }
|
||||
|
||||
现在你可以导入并使用客户端代码了。它可能是这样,并且你会发现方法有自动补全:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
要发送的载荷也会有自动补全:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image03.png">
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
请注意 `name` 和 `price` 的自动补全,它们是在 FastAPI 应用中的 `Item` 模型里定义的。
|
||||
|
||||
///
|
||||
|
||||
你发送的数据如果不符合要求,会在编辑器中显示内联错误:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image04.png">
|
||||
|
||||
响应(response)对象也拥有自动补全:
|
||||
响应对象同样有自动补全:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image05.png">
|
||||
|
||||
## 带有标签的 FastAPI 应用
|
||||
## 带有标签的 FastAPI 应用 { #fastapi-app-with-tags }
|
||||
|
||||
在许多情况下,你的FastAPI应用程序会更复杂,你可能会使用标签来分隔不同组的*路径操作(path operations)*。
|
||||
很多情况下,你的 FastAPI 应用会更大,你可能会用标签来划分不同组的*路径操作*。
|
||||
|
||||
例如,您可以有一个用 `items` 的部分和另一个用于 `users` 的部分,它们可以用标签来分隔:
|
||||
例如,你可以有一个 **items** 相关的部分和另一个 **users** 相关的部分,它们可以用标签来分隔:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
|
||||
### 生成带有标签的 TypeScript 客户端
|
||||
### 生成带标签的 TypeScript 客户端 { #generate-a-typescript-client-with-tags }
|
||||
|
||||
如果您使用标签为FastAPI应用生成客户端,它通常也会根据标签分割客户端代码。
|
||||
如果你为使用了标签的 FastAPI 应用生成客户端,通常也会根据标签来拆分客户端代码。
|
||||
|
||||
通过这种方式,您将能够为客户端代码进行正确地排序和分组:
|
||||
这样你就可以在客户端代码中把内容正确地组织和分组:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image06.png">
|
||||
|
||||
在这个案例中,您有:
|
||||
在这个例子中,你会有:
|
||||
|
||||
* `ItemsService`
|
||||
* `UsersService`
|
||||
|
||||
### 客户端方法名称
|
||||
### 客户端方法名 { #client-method-names }
|
||||
|
||||
现在生成的方法名像 `createItemItemsPost` 看起来不太简洁:
|
||||
现在,像 `createItemItemsPost` 这样的生成方法名看起来不太简洁:
|
||||
|
||||
```TypeScript
|
||||
ItemsService.createItemItemsPost({name: "Plumbus", price: 5})
|
||||
```
|
||||
|
||||
...这是因为客户端生成器为每个 *路径操作* 使用OpenAPI的内部 **操作 ID(operation ID)**。
|
||||
……这是因为客户端生成器会把每个*路径操作*的 OpenAPI 内部**操作 ID(operation ID)**用作方法名的一部分。
|
||||
|
||||
OpenAPI要求每个操作 ID 在所有 *路径操作* 中都是唯一的,因此 FastAPI 使用**函数名**、**路径**和**HTTP方法/操作**来生成此操作ID,因为这样可以确保这些操作 ID 是唯一的。
|
||||
OpenAPI 要求每个操作 ID 在所有*路径操作*中都是唯一的,因此 FastAPI 会使用**函数名**、**路径**和**HTTP 方法/操作**来生成操作 ID,以确保其唯一性。
|
||||
|
||||
但接下来我会告诉你如何改进。 🤓
|
||||
接下来我会告诉你如何改进。🤓
|
||||
|
||||
## 自定义操作ID和更好的方法名
|
||||
## 自定义操作 ID 与更好的方法名 { #custom-operation-ids-and-better-method-names }
|
||||
|
||||
您可以**修改**这些操作ID的**生成**方式,以使其更简洁,并在客户端中具有**更简洁的方法名称**。
|
||||
你可以**修改**这些操作 ID 的**生成**方式,使之更简单,从而在客户端中得到**更简洁的方法名**。
|
||||
|
||||
在这种情况下,您必须确保每个操作ID在其他方面是**唯一**的。
|
||||
在这种情况下,你需要用其他方式确保每个操作 ID 依然是**唯一**的。
|
||||
|
||||
例如,您可以确保每个*路径操作*都有一个标签,然后根据**标签**和*路径操作***名称**(函数名)来生成操作ID。
|
||||
例如,你可以确保每个*路径操作*都有一个标签,然后基于**标签**和*路径操作***名称**(函数名)来生成操作 ID。
|
||||
|
||||
### 自定义生成唯一ID函数
|
||||
### 自定义唯一 ID 生成函数 { #custom-generate-unique-id-function }
|
||||
|
||||
FastAPI为每个*路径操作*使用一个**唯一ID**,它用于**操作ID**,也用于任何所需自定义模型的名称,用于请求或响应。
|
||||
FastAPI 为每个*路径操作*使用一个**唯一 ID**,它既用于**操作 ID**,也用于请求或响应里任何需要的自定义模型名称。
|
||||
|
||||
你可以自定义该函数。它接受一个 `APIRoute` 对象作为输入,并输出一个字符串。
|
||||
你可以自定义这个函数。它接收一个 `APIRoute` 并返回一个字符串。
|
||||
|
||||
例如,以下是一个示例,它使用第一个标签(你可能只有一个标签)和*路径操作*名称(函数名)。
|
||||
例如,这里使用第一个标签(你很可能只有一个标签)和*路径操作*名称(函数名)。
|
||||
|
||||
然后,你可以将这个自定义函数作为 `generate_unique_id_function` 参数传递给 **FastAPI**:
|
||||
然后你可以把这个自定义函数通过 `generate_unique_id_function` 参数传给 **FastAPI**:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
|
||||
### 使用自定义操作ID生成TypeScript客户端
|
||||
### 使用自定义操作 ID 生成 TypeScript 客户端 { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
现在,如果你再次生成客户端,你会发现它具有改善的方法名称:
|
||||
现在再次生成客户端,你会看到方法名已经改进:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image07.png">
|
||||
|
||||
正如你所见,现在方法名称中只包含标签和函数名,不再包含URL路径和HTTP操作的信息。
|
||||
如你所见,方法名现在由标签和函数名组成,不再包含 URL 路径和 HTTP 操作的信息。
|
||||
|
||||
### 预处理用于客户端生成器的OpenAPI规范
|
||||
### 为客户端生成器预处理 OpenAPI 规范 { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
生成的代码仍然存在一些**重复的信息**。
|
||||
生成的代码中仍有一些**重复信息**。
|
||||
|
||||
我们已经知道该方法与 **items** 相关,因为它在 `ItemsService` 中(从标签中获取),但方法名中仍然有标签名作为前缀。😕
|
||||
我们已经知道这个方法与 **items** 有关,因为它位于 `ItemsService`(来自标签),但方法名里仍然带有标签名前缀。😕
|
||||
|
||||
一般情况下对于OpenAPI,我们可能仍然希望保留它,因为这将确保操作ID是**唯一的**。
|
||||
通常我们仍然希望在 OpenAPI 中保留它,以确保操作 ID 的**唯一性**。
|
||||
|
||||
但对于生成的客户端,我们可以在生成客户端之前**修改** OpenAPI 操作ID,以使方法名称更加美观和**简洁**。
|
||||
但对于生成的客户端,我们可以在生成之前**修改** OpenAPI 的操作 ID,只是为了让方法名更美观、更**简洁**。
|
||||
|
||||
我们可以将 OpenAPI JSON 下载到一个名为`openapi.json`的文件中,然后使用以下脚本**删除此前缀的标签**:
|
||||
我们可以把 OpenAPI JSON 下载到 `openapi.json` 文件中,然后用如下脚本**移除这个标签前缀**:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
|
||||
通过这样做,操作ID将从类似于 `items-get_items` 的名称重命名为 `get_items` ,这样客户端生成器就可以生成更简洁的方法名称。
|
||||
//// tab | Node.js
|
||||
|
||||
### 使用预处理的OpenAPI生成TypeScript客户端
|
||||
|
||||
现在,由于最终结果保存在文件openapi.json中,你可以修改 package.json 文件以使用此本地文件,例如:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```Javascript
|
||||
{!> ../../docs_src/generate_clients/tutorial004.js!}
|
||||
```
|
||||
|
||||
生成新的客户端之后,你现在将拥有**清晰的方法名称**,具备**自动补全**、**错误提示**等功能:
|
||||
////
|
||||
|
||||
这样,操作 ID 会从 `items-get_items` 之类的名字重命名为 `get_items`,从而让客户端生成器生成更简洁的方法名。
|
||||
|
||||
### 使用预处理后的 OpenAPI 生成 TypeScript 客户端 { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
因为最终结果现在保存在 `openapi.json` 中,你需要更新输入位置:
|
||||
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
|
||||
```
|
||||
|
||||
生成新客户端后,你将拥有**简洁的方法名**,并具备**自动补全**、**内联错误**等功能:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image08.png">
|
||||
|
||||
## 优点
|
||||
## 优点 { #benefits }
|
||||
|
||||
当使用自动生成的客户端时,你将获得以下的自动补全功能:
|
||||
使用自动生成的客户端时,你会获得以下内容的**自动补全**:
|
||||
|
||||
* 方法。
|
||||
* 请求体中的数据、查询参数等。
|
||||
* 响应数据。
|
||||
* 方法
|
||||
* 请求体中的数据、查询参数等
|
||||
* 响应数据
|
||||
|
||||
你还将获得针对所有内容的错误提示。
|
||||
你还会为所有内容获得**内联错误**。
|
||||
|
||||
每当你更新后端代码并**重新生成**前端代码时,新的*路径操作*将作为方法可用,旧的方法将被删除,并且其他任何更改将反映在生成的代码中。 🤓
|
||||
每当你更新后端代码并**重新生成**前端时,新的*路径操作*会作为方法可用,旧的方法会被移除,其他任何更改都会反映到生成的代码中。🤓
|
||||
|
||||
这也意味着如果有任何更改,它将自动**反映**在客户端代码中。如果你**构建**客户端,在使用的数据上存在**不匹配**时,它将报错。
|
||||
这也意味着如果有任何变更,它会自动**反映**到客户端代码中。而当你**构建**客户端时,如果所用数据存在任何**不匹配**,它会直接报错。
|
||||
|
||||
因此,你将在开发周期的早期**检测到许多错误**,而不必等待错误在生产环境中向最终用户展示,然后尝试调试问题所在。 ✨
|
||||
因此,你可以在开发周期的早期就**发现许多错误**,而不必等到错误在生产环境中暴露给最终用户后再去调试问题所在。✨
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
# 高级用户指南
|
||||
# 高级用户指南 { #advanced-user-guide }
|
||||
|
||||
## 额外特性
|
||||
## 附加功能 { #additional-features }
|
||||
|
||||
主要的教程 [教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank} 应该足以让你了解 **FastAPI** 的所有主要特性。
|
||||
主要的[教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank}足以带你了解 **FastAPI** 的所有主要特性。
|
||||
|
||||
你会在接下来的章节中了解到其他的选项、配置以及额外的特性。
|
||||
在接下来的章节中,你将看到其他选项、配置和附加功能。
|
||||
|
||||
/// tip
|
||||
/// tip | 提示
|
||||
|
||||
接下来的章节**并不一定是**「高级的」。
|
||||
接下来的章节不一定是“高级”的。
|
||||
|
||||
而且对于你的使用场景来说,解决方案很可能就在其中。
|
||||
对于你的用例,解决方案很可能就在其中之一。
|
||||
|
||||
///
|
||||
|
||||
## 先阅读教程
|
||||
## 先阅读教程 { #read-the-tutorial-first }
|
||||
|
||||
你可能仍会用到 **FastAPI** 主教程 [教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank} 中的大多数特性。
|
||||
仅凭主要[教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank}中的知识,你已经可以使用 **FastAPI** 的大多数功能。
|
||||
|
||||
接下来的章节我们认为你已经读过 [教程 - 用户指南](../tutorial/index.md){.internal-link target=_blank},并且假设你已经知晓其中主要思想。
|
||||
接下来的章节默认你已经读过它,并理解其中的核心概念。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 高级中间件
|
||||
# 高级中间件 { #advanced-middleware }
|
||||
|
||||
用户指南介绍了如何为应用添加[自定义中间件](../tutorial/middleware.md){.internal-link target=_blank} 。
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
本章学习如何使用其它中间件。
|
||||
|
||||
## 添加 ASGI 中间件
|
||||
## 添加 ASGI 中间件 { #adding-asgi-middlewares }
|
||||
|
||||
因为 **FastAPI** 基于 Starlette,且执行 <abbr title="Asynchronous Server Gateway Interface,异步服务器网关界面">ASGI</abbr> 规范,所以可以使用任意 ASGI 中间件。
|
||||
因为 **FastAPI** 基于 Starlette,且执行 <abbr title="Asynchronous Server Gateway Interface - 异步服务器网关接口">ASGI</abbr> 规范,所以可以使用任意 ASGI 中间件。
|
||||
|
||||
中间件不必是专为 FastAPI 或 Starlette 定制的,只要遵循 ASGI 规范即可。
|
||||
|
||||
@@ -39,11 +39,11 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
`app.add_middleware()` 的第一个参数是中间件的类,其它参数则是要传递给中间件的参数。
|
||||
|
||||
## 集成中间件
|
||||
## 集成中间件 { #integrated-middlewares }
|
||||
|
||||
**FastAPI** 为常见用例提供了一些中间件,下面介绍怎么使用这些中间件。
|
||||
|
||||
/// note | 技术细节
|
||||
/// note | 注意
|
||||
|
||||
以下几个示例中也可以使用 `from starlette.middleware.something import SomethingMiddleware`。
|
||||
|
||||
@@ -51,45 +51,47 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
///
|
||||
|
||||
## `HTTPSRedirectMiddleware`
|
||||
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
|
||||
|
||||
强制所有传入请求必须是 `https` 或 `wss`。
|
||||
|
||||
任何传向 `http` 或 `ws` 的请求都会被重定向至安全方案。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
强制所有传入请求都必须正确设置 `Host` 请求头,以防 HTTP 主机头攻击。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
|
||||
支持以下参数:
|
||||
|
||||
* `allowed_hosts` - 允许的域名(主机名)列表。`*.example.com` 等通配符域名可以匹配子域名,或使用 `allowed_hosts=["*"]` 允许任意主机名,或省略中间件。
|
||||
* `allowed_hosts` - 允许的域名(主机名)列表。`*.example.com` 等通配符域名可以匹配子域名。若要允许任意主机名,可使用 `allowed_hosts=["*"]` 或省略此中间件。
|
||||
* `www_redirect` - 若设置为 `True`,对允许主机的非 www 版本的请求将被重定向到其 www 版本。默认为 `True`。
|
||||
|
||||
如果传入的请求没有通过验证,则发送 `400` 响应。
|
||||
|
||||
## `GZipMiddleware`
|
||||
## `GZipMiddleware` { #gzipmiddleware }
|
||||
|
||||
处理 `Accept-Encoding` 请求头中包含 `gzip` 请求的 GZip 响应。
|
||||
处理 `Accept-Encoding` 请求头中包含 `"gzip"` 请求的 GZip 响应。
|
||||
|
||||
中间件会处理标准响应与流响应。
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
|
||||
支持以下参数:
|
||||
|
||||
* `minimum_size` - 小于最小字节的响应不使用 GZip。 默认值是 `500`。
|
||||
* `minimum_size` - 小于该最小字节数的响应不使用 GZip。默认值是 `500`。
|
||||
* `compresslevel` - GZip 压缩使用的级别,为 1 到 9 的整数。默认为 `9`。值越低压缩越快但文件更大,值越高压缩越慢但文件更小。
|
||||
|
||||
## 其它中间件
|
||||
## 其它中间件 { #other-middlewares }
|
||||
|
||||
除了上述中间件外,FastAPI 还支持其它ASGI 中间件。
|
||||
|
||||
例如:
|
||||
|
||||
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn 的 `ProxyHeadersMiddleware`</a>
|
||||
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn 的 `ProxyHeadersMiddleware`</a>
|
||||
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
|
||||
|
||||
其它可用中间件详见 <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Starlette 官档 - 中间件</a> 及 <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome 列表</a>。
|
||||
其它可用中间件详见 <a href="https://www.starlette.dev/middleware/" class="external-link" target="_blank">Starlette 官档 - 中间件</a> 及 <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome 列表</a>。
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# OpenAPI 回调
|
||||
# OpenAPI 回调 { #openapi-callbacks }
|
||||
|
||||
您可以创建触发外部 API 请求的*路径操作* API,这个外部 API 可以是别人创建的,也可以是由您自己创建的。
|
||||
您可以创建一个包含*路径操作*的 API,它会触发对别人创建的*外部 API*的请求(很可能就是那个会“使用”您 API 的同一个开发者)。
|
||||
|
||||
API 应用调用外部 API 时的流程叫做**回调**。因为外部开发者编写的软件发送请求至您的 API,然后您的 API 要进行回调,并把请求发送至外部 API。
|
||||
当您的 API 应用调用*外部 API*时,这个过程被称为“回调”。因为外部开发者编写的软件会先向您的 API 发送请求,然后您的 API 再进行*回调*,向*外部 API*发送请求(很可能也是该开发者创建的)。
|
||||
|
||||
此时,我们需要存档外部 API 的*信息*,比如应该有哪些*路径操作*,返回什么样的请求体,应该返回哪种响应等。
|
||||
此时,我们需要存档外部 API 的*信息*,比如应该有哪些*路径操作*,请求体应该是什么,应该返回什么响应等。
|
||||
|
||||
## 使用回调的应用
|
||||
## 使用回调的应用 { #an-app-with-callbacks }
|
||||
|
||||
示例如下。
|
||||
|
||||
@@ -14,16 +14,16 @@ API 应用调用外部 API 时的流程叫做**回调**。因为外部开发者
|
||||
|
||||
发票包括 `id`、`title`(可选)、`customer`、`total` 等属性。
|
||||
|
||||
API 的用户 (外部开发者)要在您的 API 内使用 POST 请求创建一条发票记录。
|
||||
API 的用户(外部开发者)要在您的 API 内使用 POST 请求创建一条发票记录。
|
||||
|
||||
(假设)您的 API 将:
|
||||
|
||||
* 把发票发送至外部开发者的消费者
|
||||
* 归集现金
|
||||
* 把通知发送至 API 的用户(外部开发者)
|
||||
* 通过(从您的 API)发送 POST 请求至外部 API (即**回调**)来完成
|
||||
* 通过(从您的 API)发送 POST 请求至外部 API(即**回调**)来完成
|
||||
|
||||
## 常规 **FastAPI** 应用
|
||||
## 常规 **FastAPI** 应用 { #the-normal-fastapi-app }
|
||||
|
||||
添加回调前,首先看下常规 API 应用是什么样子。
|
||||
|
||||
@@ -31,17 +31,17 @@ API 的用户 (外部开发者)要在您的 API 内使用 POST 请求创建
|
||||
|
||||
这部分代码很常规,您对绝大多数代码应该都比较熟悉了:
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[10:14,37:54] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
`callback_url` 查询参数使用 Pydantic 的 <a href="https://pydantic-docs.helpmanual.io/usage/types/#urls" class="external-link" target="_blank">URL</a> 类型。
|
||||
`callback_url` 查询参数使用 Pydantic 的 <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a> 类型。
|
||||
|
||||
///
|
||||
|
||||
此处唯一比较新的内容是*路径操作装饰器*中的 `callbacks=invoices_callback_router.routes` 参数,下文介绍。
|
||||
|
||||
## 存档回调
|
||||
## 存档回调 { #documenting-the-callback }
|
||||
|
||||
实际的回调代码高度依赖于您自己的 API 应用。
|
||||
|
||||
@@ -51,14 +51,14 @@ API 的用户 (外部开发者)要在您的 API 内使用 POST 请求创建
|
||||
|
||||
```Python
|
||||
callback_url = "https://example.com/api/v1/invoices/events/"
|
||||
requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
```
|
||||
|
||||
但回调最重要的部分可能是,根据 API 要发送给回调请求体的数据等内容,确保您的 API 用户(外部开发者)正确地实现*外部 API*。
|
||||
|
||||
因此,我们下一步要做的就是添加代码,为从 API 接收回调的*外部 API*存档。
|
||||
|
||||
这部分文档在 `/docs` 下的 Swagger API 文档中显示,并且会告诉外部开发者如何构建*外部 API*。
|
||||
这部分文档在 `/docs` 下的 Swagger UI 中显示,并且会告诉外部开发者如何构建*外部 API*。
|
||||
|
||||
本例没有实现回调本身(只是一行代码),只有文档部分。
|
||||
|
||||
@@ -66,17 +66,17 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
|
||||
实际的回调只是 HTTP 请求。
|
||||
|
||||
实现回调时,要使用 <a href="https://www.encode.io/httpx/" class="external-link" target="_blank">HTTPX</a> 或 <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>。
|
||||
实现回调时,要使用 <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> 或 <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requests</a>。
|
||||
|
||||
///
|
||||
|
||||
## 编写回调文档代码
|
||||
## 编写回调文档代码 { #write-the-callback-documentation-code }
|
||||
|
||||
应用不执行这部分代码,只是用它来*记录 外部 API* 。
|
||||
|
||||
但,您已经知道用 **FastAPI** 创建自动 API 文档有多简单了。
|
||||
|
||||
我们要使用与存档*外部 API* 相同的知识……通过创建外部 API 要实现的*路径操作*(您的 API 要调用的)。
|
||||
我们要使用与存档*外部 API* 相同的知识...通过创建外部 API 要实现的*路径操作*(您的 API 要调用的)。
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
@@ -86,13 +86,13 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
|
||||
///
|
||||
|
||||
### 创建回调的 `APIRouter`
|
||||
### 创建回调的 `APIRouter` { #create-a-callback-apirouter }
|
||||
|
||||
首先,新建包含一些用于回调的 `APIRouter`。
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[5,26] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
|
||||
|
||||
### 创建回调*路径操作*
|
||||
### 创建回调*路径操作* { #create-the-callback-path-operation }
|
||||
|
||||
创建回调*路径操作*也使用之前创建的 `APIRouter`。
|
||||
|
||||
@@ -101,16 +101,16 @@ requests.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
* 声明要接收的请求体,例如,`body: InvoiceEvent`
|
||||
* 还要声明要返回的响应,例如,`response_model=InvoiceEventReceived`
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[17:19,22:23,29:33] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
|
||||
|
||||
回调*路径操作*与常规*路径操作*有两点主要区别:
|
||||
|
||||
* 它不需要任何实际的代码,因为应用不会调用这段代码。它只是用于存档*外部 API*。因此,函数的内容只需要 `pass` 就可以了
|
||||
* *路径*可以包含 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" class="external-link" target="_blank">OpenAPI 3 表达式</a>(详见下文),可以使用带参数的变量,以及发送至您的 API 的原始请求的部分
|
||||
* *路径*可以包含 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 表达式</a>(详见下文),可以使用带参数的变量,以及发送至您的 API 的原始请求的部分
|
||||
|
||||
### 回调路径表达式
|
||||
### 回调路径表达式 { #the-callback-path-expression }
|
||||
|
||||
回调*路径*支持包含发送给您的 API 的原始请求的部分的 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#key-expression" class="external-link" target="_blank">OpenAPI 3 表达式</a>。
|
||||
回调*路径*支持包含发送给您的 API 的原始请求的部分的 <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">OpenAPI 3 表达式</a>。
|
||||
|
||||
本例中是**字符串**:
|
||||
|
||||
@@ -159,17 +159,17 @@ JSON 请求体包含如下内容:
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
注意,回调 URL包含 `callback_url` (`https://www.external.org/events`)中的查询参数,还有 JSON 请求体内部的发票 ID(`2expen51ve`)。
|
||||
注意,回调 URL 包含 `callback_url`(`https://www.external.org/events`)中的查询参数,还有 JSON 请求体内部的发票 ID(`2expen51ve`)。
|
||||
|
||||
///
|
||||
|
||||
### 添加回调路由
|
||||
### 添加回调路由 { #add-the-callback-router }
|
||||
|
||||
至此,在上文创建的回调路由里就包含了*回调路径操作*(外部开发者要在外部 API 中实现)。
|
||||
|
||||
现在使用 API *路径操作装饰器*的参数 `callbacks`,从回调路由传递属性 `.routes`(实际上只是路由/路径操作的**列表**):
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[36] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
|
||||
|
||||
/// tip | 提示
|
||||
|
||||
@@ -177,9 +177,9 @@ JSON 请求体包含如下内容:
|
||||
|
||||
///
|
||||
|
||||
### 查看文档
|
||||
### 查看文档 { #check-the-docs }
|
||||
|
||||
现在,使用 Uvicorn 启动应用,打开 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs。</a>
|
||||
现在,使用 Uvicorn 启动应用,打开 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>。
|
||||
|
||||
就能看到文档的*路径操作*已经包含了**回调**的内容以及*外部 API*:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# OpenAPI 网络钩子
|
||||
# OpenAPI 网络钩子 { #openapi-webhooks }
|
||||
|
||||
有些情况下,您可能想告诉您的 API **用户**,您的应用程序可以携带一些数据调用*他们的*应用程序(给它们发送请求),通常是为了**通知**某种**事件**。
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
这通常被称为**网络钩子**(Webhook)。
|
||||
|
||||
## 使用网络钩子的步骤
|
||||
## 使用网络钩子的步骤 { #webhooks-steps }
|
||||
|
||||
通常的过程是**您**在代码中**定义**要发送的消息,即**请求的主体**。
|
||||
|
||||
@@ -16,27 +16,27 @@
|
||||
|
||||
所有关于注册网络钩子的 URL 的**逻辑**以及发送这些请求的实际代码都由您决定。您可以在**自己的代码**中以任何想要的方式来编写它。
|
||||
|
||||
## 使用 `FastAPI` 和 OpenAPI 文档化网络钩子
|
||||
## 使用 `FastAPI` 和 OpenAPI 文档化网络钩子 { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
|
||||
使用 **FastAPI**,您可以利用 OpenAPI 来自定义这些网络钩子的名称、您的应用可以发送的 HTTP 操作类型(例如 `POST`、`PUT` 等)以及您的应用将发送的**请求体**。
|
||||
|
||||
这能让您的用户更轻松地**实现他们的 API** 来接收您的**网络钩子**请求,他们甚至可能能够自动生成一些自己的 API 代码。
|
||||
|
||||
/// info
|
||||
/// info | 信息
|
||||
|
||||
网络钩子在 OpenAPI 3.1.0 及以上版本中可用,FastAPI `0.99.0` 及以上版本支持。
|
||||
|
||||
///
|
||||
|
||||
## 带有网络钩子的应用程序
|
||||
## 带有网络钩子的应用程序 { #an-app-with-webhooks }
|
||||
|
||||
当您创建一个 **FastAPI** 应用程序时,有一个 `webhooks` 属性可以用来定义网络钩子,方式与您定义*路径操作*的时候相同,例如使用 `@app.webhooks.post()` 。
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
|
||||
您定义的网络钩子将被包含在 `OpenAPI` 的架构中,并出现在自动生成的**文档 UI** 中。
|
||||
|
||||
/// info
|
||||
/// info | 信息
|
||||
|
||||
`app.webhooks` 对象实际上只是一个 `APIRouter` ,与您在使用多个文件来构建应用程序时所使用的类型相同。
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
这是因为我们预计**您的用户**会以其他方式(例如通过网页仪表板)来定义他们希望接收网络钩子的请求的实际 **URL 路径**。
|
||||
|
||||
### 查看文档
|
||||
### 查看文档 { #check-the-docs }
|
||||
|
||||
现在您可以启动您的应用程序并访问 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# 路径操作的高级配置
|
||||
# 路径操作的高级配置 { #path-operation-advanced-configuration }
|
||||
|
||||
## OpenAPI 的 operationId
|
||||
## OpenAPI 的 operationId { #openapi-operationid }
|
||||
|
||||
/// warning
|
||||
|
||||
如果你并非 OpenAPI 的「专家」,你可能不需要这部分内容。
|
||||
如果你并非 OpenAPI 的“专家”,你可能不需要这部分内容。
|
||||
|
||||
///
|
||||
|
||||
你可以在路径操作中通过参数 `operation_id` 设置要使用的 OpenAPI `operationId`。
|
||||
你可以在 *路径操作* 中通过参数 `operation_id` 设置要使用的 OpenAPI `operationId`。
|
||||
|
||||
务必确保每个操作路径的 `operation_id` 都是唯一的。
|
||||
务必确保每个操作的 `operation_id` 都是唯一的。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
|
||||
### 使用 *路径操作函数* 的函数名作为 operationId
|
||||
### 使用 *路径操作函数* 的函数名作为 operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
如果你想用你的 API 的函数名作为 `operationId` 的名字,你可以遍历一遍 API 的函数名,然后使用他们的 `APIRoute.name` 重写每个 *路径操作* 的 `operation_id`。
|
||||
如果你想用 API 的函数名作为 `operationId`,你可以遍历所有路径操作,并使用它们的 `APIRoute.name` 重写每个 *路径操作* 的 `operation_id`。
|
||||
|
||||
你应该在添加了所有 *路径操作* 之后执行此操作。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2,12,13,14,15,16,17,18,19,20,21,24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip
|
||||
|
||||
@@ -36,19 +36,137 @@
|
||||
|
||||
///
|
||||
|
||||
## 从 OpenAPI 中排除
|
||||
## 从 OpenAPI 中排除 { #exclude-from-openapi }
|
||||
|
||||
使用参数 `include_in_schema` 并将其设置为 `False` ,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了)。
|
||||
使用参数 `include_in_schema` 并将其设置为 `False`,来从生成的 OpenAPI 方案中排除一个 *路径操作*(这样一来,就从自动化文档系统中排除掉了):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
|
||||
## docstring 的高级描述
|
||||
## 来自 docstring 的高级描述 { #advanced-description-from-docstring }
|
||||
|
||||
你可以限制 *路径操作函数* 的 `docstring` 中用于 OpenAPI 的行数。
|
||||
|
||||
添加一个 `\f` (一个「换页」的转义字符)可以使 **FastAPI** 在那一位置截断用于 OpenAPI 的输出。
|
||||
添加一个 `\f`(一个“换页”的转义字符)可以使 **FastAPI** 在那一位置截断用于 OpenAPI 的输出。
|
||||
|
||||
剩余部分不会出现在文档中,但是其他工具(比如 Sphinx)可以使用剩余部分。
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19,20,21,22,23,24,25,26,27,28,29] *}
|
||||
## 附加响应 { #additional-responses }
|
||||
|
||||
你可能已经见过如何为一个 *路径操作* 声明 `response_model` 和 `status_code`。
|
||||
|
||||
这定义了该 *路径操作* 主响应的元数据。
|
||||
|
||||
你也可以为它声明带有各自模型、状态码等的附加响应。
|
||||
|
||||
文档中有一个完整章节,你可以阅读这里的[OpenAPI 中的附加响应](additional-responses.md){.internal-link target=_blank}。
|
||||
|
||||
## OpenAPI Extra { #openapi-extra }
|
||||
|
||||
当你在应用中声明一个 *路径操作* 时,**FastAPI** 会自动生成与该 *路径操作* 相关的元数据,以包含到 OpenAPI 方案中。
|
||||
|
||||
/// note | 技术细节
|
||||
|
||||
在 OpenAPI 规范中,这被称为 <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Operation 对象</a>。
|
||||
|
||||
///
|
||||
|
||||
它包含关于该 *路径操作* 的所有信息,并用于生成自动文档。
|
||||
|
||||
它包括 `tags`、`parameters`、`requestBody`、`responses` 等。
|
||||
|
||||
这个特定于 *路径操作* 的 OpenAPI 方案通常由 **FastAPI** 自动生成,但你也可以扩展它。
|
||||
|
||||
/// tip
|
||||
|
||||
这是一个较低层级的扩展点。
|
||||
|
||||
如果你只需要声明附加响应,更方便的方式是使用[OpenAPI 中的附加响应](additional-responses.md){.internal-link target=_blank}。
|
||||
|
||||
///
|
||||
|
||||
你可以使用参数 `openapi_extra` 扩展某个 *路径操作* 的 OpenAPI 方案。
|
||||
|
||||
### OpenAPI 扩展 { #openapi-extensions }
|
||||
|
||||
例如,这个 `openapi_extra` 可用于声明 [OpenAPI 扩展](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
|
||||
当你打开自动 API 文档时,你的扩展会显示在该 *路径操作* 的底部。
|
||||
|
||||
<img src="/img/tutorial/path-operation-advanced-configuration/image01.png">
|
||||
|
||||
如果你查看最终生成的 OpenAPI(在你的 API 的 `/openapi.json`),你也会看到你的扩展作为该 *路径操作* 的一部分:
|
||||
|
||||
```JSON hl_lines="22"
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "FastAPI",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-aperture-labs-portal": "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义 OpenAPI 路径操作方案 { #custom-openapi-path-operation-schema }
|
||||
|
||||
`openapi_extra` 中的字典会与该 *路径操作* 自动生成的 OpenAPI 方案进行深度合并。
|
||||
|
||||
因此,你可以在自动生成的方案上添加额外数据。
|
||||
|
||||
例如,你可以决定用自己的代码读取并验证请求,而不使用 FastAPI 与 Pydantic 的自动功能,但你仍然希望在 OpenAPI 方案中定义该请求。
|
||||
|
||||
你可以用 `openapi_extra` 来做到:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
|
||||
在这个示例中,我们没有声明任何 Pydantic 模型。事实上,请求体甚至没有被 <abbr title="从某种纯文本格式(如字节)转换为 Python 对象">解析</abbr> 为 JSON,而是直接以 `bytes` 读取,并由函数 `magic_data_reader()` 以某种方式负责解析。
|
||||
|
||||
尽管如此,我们仍然可以声明请求体的预期方案。
|
||||
|
||||
### 自定义 OpenAPI 内容类型 { #custom-openapi-content-type }
|
||||
|
||||
使用同样的技巧,你可以用一个 Pydantic 模型来定义 JSON Schema,然后把它包含到该 *路径操作* 的自定义 OpenAPI 方案部分中。
|
||||
|
||||
即使请求中的数据类型不是 JSON,你也可以这样做。
|
||||
|
||||
例如,在这个应用中我们不使用 FastAPI 集成的从 Pydantic 模型提取 JSON Schema 的功能,也不使用对 JSON 的自动校验。实际上,我们将请求的内容类型声明为 YAML,而不是 JSON:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
|
||||
尽管我们没有使用默认的集成功能,我们仍然使用 Pydantic 模型手动生成我们想以 YAML 接收的数据的 JSON Schema。
|
||||
|
||||
然后我们直接使用请求并将请求体提取为 `bytes`。这意味着 FastAPI 甚至不会尝试将请求负载解析为 JSON。
|
||||
|
||||
接着在我们的代码中,我们直接解析该 YAML 内容,然后再次使用同一个 Pydantic 模型来验证该 YAML 内容:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
|
||||
/// tip
|
||||
|
||||
这里我们复用了同一个 Pydantic 模型。
|
||||
|
||||
但同样地,我们也可以用其他方式对其进行验证。
|
||||
|
||||
///
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 响应 - 更改状态码
|
||||
# 响应 - 更改状态码 { #response-change-status-code }
|
||||
|
||||
你可能之前已经了解到,你可以设置默认的[响应状态码](../tutorial/response-status-code.md){.internal-link target=_blank}。
|
||||
|
||||
但在某些情况下,你需要返回一个不同于默认值的状态码。
|
||||
|
||||
## 使用场景
|
||||
## 使用场景 { #use-case }
|
||||
|
||||
例如,假设你想默认返回一个HTTP状态码为“OK”`200`。
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
|
||||
对于这些情况,你可以使用一个`Response`参数。
|
||||
|
||||
## 使用 `Response` 参数
|
||||
## 使用 `Response` 参数 { #use-a-response-parameter }
|
||||
|
||||
你可以在你的*路径操作函数*中声明一个`Response`类型的参数(就像你可以为cookies和头部做的那样)。
|
||||
|
||||
然后你可以在这个*临时*响应对象中设置`status_code`。
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
|
||||
然后你可以像平常一样返回任何你需要的对象(例如一个`dict`或者一个数据库模型)。如果你声明了一个`response_model`,它仍然会被用来过滤和转换你返回的对象。
|
||||
|
||||
**FastAPI**将使用这个临时响应来提取状态码(也包括cookies和头部),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。
|
||||
**FastAPI**将使用这个*临时*响应来提取状态码(也包括cookies和头部),并将它们放入包含你返回的值的最终响应中,该响应由任何`response_model`过滤。
|
||||
|
||||
你也可以在依赖项中声明`Response`参数,并在其中设置状态码。但请注意,最后设置的状态码将会生效。
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user