mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into deferred-init
Signed-off-by: Jan Vollmer <jan@vllmr.dev>
This commit is contained in:
commit
53363f0d0e
|
|
@ -29,8 +29,6 @@ internal:
|
|||
- scripts/**
|
||||
- .gitignore
|
||||
- .pre-commit-config.yaml
|
||||
- pdm_build.py
|
||||
- requirements*.txt
|
||||
- uv.lock
|
||||
- docs/en/data/sponsors.yml
|
||||
- docs/en/overrides/main.html
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ on:
|
|||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- fastapi
|
||||
- fastapi-slim
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
|
@ -26,14 +21,9 @@ jobs:
|
|||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version-file: ".python-version"
|
||||
# Issue ref: https://github.com/actions/setup-python/issues/436
|
||||
# cache: "pip"
|
||||
# cache-dependency-path: pyproject.toml
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
- name: Build distribution
|
||||
run: uv build
|
||||
env:
|
||||
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
|
||||
- name: Publish
|
||||
run: uv publish
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
test:
|
||||
needs:
|
||||
- changes
|
||||
if: needs.changes.outputs.src == 'true'
|
||||
if: needs.changes.outputs.src == 'true' || github.ref == 'refs/heads/master'
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, macos-latest ]
|
||||
|
|
@ -68,10 +68,8 @@ jobs:
|
|||
python-version: "3.13"
|
||||
coverage: coverage
|
||||
uv-resolution: highest
|
||||
# Ubuntu with 3.13 needs coverage for CodSpeed benchmarks
|
||||
- os: ubuntu-latest
|
||||
python-version: "3.13"
|
||||
coverage: coverage
|
||||
uv-resolution: highest
|
||||
codspeed: codspeed
|
||||
- os: ubuntu-latest
|
||||
|
|
@ -109,20 +107,10 @@ jobs:
|
|||
run: uv pip install "git+https://github.com/Kludex/starlette@main"
|
||||
- run: mkdir coverage
|
||||
- name: Test
|
||||
if: matrix.codspeed != 'codspeed'
|
||||
run: uv run --no-sync bash scripts/test.sh
|
||||
env:
|
||||
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
|
||||
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
|
||||
- name: CodSpeed benchmarks
|
||||
if: matrix.codspeed == 'codspeed'
|
||||
uses: CodSpeedHQ/action@v4
|
||||
env:
|
||||
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
|
||||
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
|
||||
with:
|
||||
mode: simulation
|
||||
run: uv run --no-sync coverage run -m pytest tests/ --codspeed
|
||||
# Do not store coverage for all possible combinations to avoid file size max errors in Smokeshow
|
||||
- name: Store coverage files
|
||||
if: matrix.coverage == 'coverage'
|
||||
|
|
@ -132,6 +120,39 @@ jobs:
|
|||
path: coverage
|
||||
include-hidden-files: true
|
||||
|
||||
benchmark:
|
||||
needs:
|
||||
- changes
|
||||
if: needs.changes.outputs.src == 'true' || github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
UV_PYTHON: "3.13"
|
||||
UV_RESOLUTION: highest
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Setup uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: |
|
||||
pyproject.toml
|
||||
uv.lock
|
||||
- name: Install Dependencies
|
||||
run: uv sync --no-dev --group tests --extra all
|
||||
- name: CodSpeed benchmarks
|
||||
uses: CodSpeedHQ/action@v4
|
||||
with:
|
||||
mode: simulation
|
||||
run: uv run --no-sync pytest tests/benchmarks --codspeed
|
||||
|
||||
coverage-combine:
|
||||
needs:
|
||||
- test
|
||||
|
|
@ -176,6 +197,7 @@ jobs:
|
|||
if: always()
|
||||
needs:
|
||||
- coverage-combine
|
||||
- benchmark
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
|
|
@ -186,4 +208,4 @@ jobs:
|
|||
uses: re-actors/alls-green@release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
allowed-skips: coverage-combine,test
|
||||
allowed-skips: coverage-combine,test,benchmark
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
# Custom Response - HTML, Stream, File, others { #custom-response-html-stream-file-others }
|
||||
|
||||
By default, **FastAPI** will return the responses using `JSONResponse`.
|
||||
By default, **FastAPI** will return JSON responses.
|
||||
|
||||
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
|
|
@ -10,43 +10,27 @@ But you can also declare the `Response` that you want to be used (e.g. any `Resp
|
|||
|
||||
The contents that you return from your *path operation function* will be put inside of that `Response`.
|
||||
|
||||
And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
|
||||
|
||||
/// note
|
||||
|
||||
If you use a response class with no media type, FastAPI will expect your response to have no content, so it will not document the response format in its generated OpenAPI docs.
|
||||
|
||||
///
|
||||
|
||||
## Use `ORJSONResponse` { #use-orjsonresponse }
|
||||
## JSON Responses { #json-responses }
|
||||
|
||||
For example, if you are squeezing performance, you can install and use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> and set the response to be `ORJSONResponse`.
|
||||
By default FastAPI returns JSON responses.
|
||||
|
||||
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
|
||||
If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
|
||||
|
||||
For large responses, returning a `Response` directly is much faster than returning a dictionary.
|
||||
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
|
||||
|
||||
This is because by default, FastAPI will inspect every item inside and make sure it is serializable as JSON, using the same [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} explained in the tutorial. This is what allows you to return **arbitrary objects**, for example database models.
|
||||
If you declare a `response_class` with a JSON media type (`application/json`), like is the case with the `JSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*. But the data won't be serialized to JSON bytes with Pydantic, instead it will be converted with the `jsonable_encoder` and then passed to the `JSONResponse` class, which will serialize it to bytes using the standard JSON library in Python.
|
||||
|
||||
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.
|
||||
### JSON Performance { #json-performance }
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b_py310.py hl[2,7] *}
|
||||
In short, if you want the maximum performance, use a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} and don't declare a `response_class` in the *path operation decorator*.
|
||||
|
||||
/// info
|
||||
|
||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||
|
||||
In this case, the HTTP header `Content-Type` will be set to `application/json`.
|
||||
|
||||
And it will be documented as such in OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
/// tip
|
||||
|
||||
The `ORJSONResponse` is only available in FastAPI, not in Starlette.
|
||||
|
||||
///
|
||||
{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *}
|
||||
|
||||
## HTML Response { #html-response }
|
||||
|
||||
|
|
@ -154,40 +138,6 @@ Takes some data and returns an `application/json` encoded response.
|
|||
|
||||
This is the default response used in **FastAPI**, as you read above.
|
||||
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
A fast alternative JSON response using <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, as you read above.
|
||||
|
||||
/// info
|
||||
|
||||
This requires installing `orjson` for example with `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
An alternative JSON response using <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
/// info
|
||||
|
||||
This requires installing `ujson` for example with `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
/// warning
|
||||
|
||||
`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001_py310.py hl[2,7] *}
|
||||
|
||||
/// tip
|
||||
|
||||
It's possible that `ORJSONResponse` might be a faster alternative.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
|
||||
|
|
@ -268,7 +218,7 @@ In this case, you can return the file path directly from your *path operation* f
|
|||
|
||||
You can create your own custom response class, inheriting from `Response` and using it.
|
||||
|
||||
For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, but with some custom settings not used in the included `ORJSONResponse` class.
|
||||
For example, let's say that you want to use <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> with some settings.
|
||||
|
||||
Let's say you want it to return indented and formatted JSON, so you want to use the orjson option `orjson.OPT_INDENT_2`.
|
||||
|
||||
|
|
@ -292,13 +242,21 @@ Now instead of returning:
|
|||
|
||||
Of course, you will probably find much better ways to take advantage of this than formatting JSON. 😉
|
||||
|
||||
### `orjson` or Response Model { #orjson-or-response-model }
|
||||
|
||||
If what you are looking for is performance, you are probably better off using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than an `orjson` response.
|
||||
|
||||
With a response model, FastAPI will use Pydantic to serialize the data to JSON, without using intermediate steps, like converting it with `jsonable_encoder`, which would happen in any other case.
|
||||
|
||||
And under the hood, Pydantic uses the same underlying Rust mechanisms as `orjson` to serialize to JSON, so you will already get the best performance with a response model.
|
||||
|
||||
## Default response class { #default-response-class }
|
||||
|
||||
When creating a **FastAPI** class instance or an `APIRouter` you can specify which response class to use by default.
|
||||
|
||||
The parameter that defines this is `default_response_class`.
|
||||
|
||||
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
|
||||
In the example below, **FastAPI** will use `HTMLResponse` by default, in all *path operations*, instead of JSON.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
# JSON with Bytes as Base64 { #json-with-bytes-as-base64 }
|
||||
|
||||
If your app needs to receive and send JSON data, but you need to include binary data in it, you can encode it as base64.
|
||||
|
||||
## Base64 vs Files { #base64-vs-files }
|
||||
|
||||
Consider first if you can use [Request Files](../tutorial/request-files.md){.internal-link target=_blank} for uploading binary data and [Custom Response - FileResponse](./custom-response.md#fileresponse--fileresponse-){.internal-link target=_blank} for sending binary data, instead of encoding it in JSON.
|
||||
|
||||
JSON can only contain UTF-8 encoded strings, so it can't contain raw bytes.
|
||||
|
||||
Base64 can encode binary data in strings, but to do it, it needs to use more characters than the original binary data, so it would normally be less efficient than regular files.
|
||||
|
||||
Use base64 only if you definitely need to include binary data in JSON, and you can't use files for that.
|
||||
|
||||
## Pydantic `bytes` { #pydantic-bytes }
|
||||
|
||||
You can declare a Pydantic model with `bytes` fields, and then use `val_json_bytes` in the model config to tell it to use base64 to *validate* input JSON data, as part of that validation it will decode the base64 string into bytes.
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *}
|
||||
|
||||
If you check the `/docs`, they will show that the field `data` expects base64 encoded bytes:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/json-base64-bytes/image01.png">
|
||||
</div>
|
||||
|
||||
You could send a request like:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Some data",
|
||||
"data": "aGVsbG8="
|
||||
}
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
`aGVsbG8=` is the base64 encoding of `hello`.
|
||||
|
||||
///
|
||||
|
||||
And then Pydantic will decode the base64 string and give you the original bytes in the `data` field of the model.
|
||||
|
||||
You will receive a response like:
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Some data",
|
||||
"content": "hello"
|
||||
}
|
||||
```
|
||||
|
||||
## Pydantic `bytes` for Output Data { #pydantic-bytes-for-output-data }
|
||||
|
||||
You can also use `bytes` fields with `ser_json_bytes` in the model config for output data, and Pydantic will *serialize* the bytes as base64 when generating the JSON response.
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *}
|
||||
|
||||
## Pydantic `bytes` for Input and Output Data { #pydantic-bytes-for-input-and-output-data }
|
||||
|
||||
And of course, you can use the same model configured to use base64 to handle both input (*validate*) with `val_json_bytes` and output (*serialize*) with `ser_json_bytes` when receiving and sending JSON data.
|
||||
|
||||
{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *}
|
||||
|
|
@ -2,19 +2,23 @@
|
|||
|
||||
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
|
||||
|
||||
By default, **FastAPI** would automatically convert that return value to JSON using the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}.
|
||||
If you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} FastAPI will use it to serialize the data to JSON, using Pydantic.
|
||||
|
||||
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a `JSONResponse` that would be used to send the response to the client.
|
||||
If you don't declare a response model, FastAPI will use the `jsonable_encoder` explained in [JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank} and put it in a `JSONResponse`.
|
||||
|
||||
But you can return a `JSONResponse` directly from your *path operations*.
|
||||
You could also create a `JSONResponse` directly and return it.
|
||||
|
||||
It might be useful, for example, to return custom headers or cookies.
|
||||
/// tip
|
||||
|
||||
You will normally have much better performance using a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} than returning a `JSONResponse` directly, as that way it serializes the data using Pydantic, in Rust.
|
||||
|
||||
///
|
||||
|
||||
## Return a `Response` { #return-a-response }
|
||||
|
||||
In fact, you can return any `Response` or any sub-class of it.
|
||||
You can return any `Response` or any sub-class of it.
|
||||
|
||||
/// tip
|
||||
/// info
|
||||
|
||||
`JSONResponse` itself is a sub-class of `Response`.
|
||||
|
||||
|
|
@ -56,6 +60,18 @@ You could put your XML content in a string, put that in a `Response`, and return
|
|||
|
||||
{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *}
|
||||
|
||||
## How a Response Model Works { #how-a-response-model-works }
|
||||
|
||||
When you declare a [Response Model](../tutorial/response-model.md){.internal-link target=_blank} in a path operation, **FastAPI** will use it to serialize the data to JSON, using Pydantic.
|
||||
|
||||
{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *}
|
||||
|
||||
As that will happen on the Rust side, the performance will be much better than if it was done with regular Python and the `JSONResponse` class.
|
||||
|
||||
When using a response model FastAPI won't use the `jsonable_encoder` to convert the data (which would be slower) nor the `JSONResponse` class.
|
||||
|
||||
Instead it takes the JSON bytes generated with Pydantic using the response model and returns a `Response` with the right media type for JSON directly (`application/json`).
|
||||
|
||||
## Notes { #notes }
|
||||
|
||||
When you return a `Response` directly its data is not validated, converted (serialized), or documented automatically.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
# Strict Content-Type Checking { #strict-content-type-checking }
|
||||
|
||||
By default, **FastAPI** uses strict `Content-Type` header checking for JSON request bodies, this means that JSON requests **must** include a valid `Content-Type` header (e.g. `application/json`) in order for the body to be parsed as JSON.
|
||||
|
||||
## CSRF Risk { #csrf-risk }
|
||||
|
||||
This default behavior provides protection against a class of **Cross-Site Request Forgery (CSRF)** attacks in a very specific scenario.
|
||||
|
||||
These attacks exploit the fact that browsers allow scripts to send requests without doing any CORS preflight check when they:
|
||||
|
||||
* don't have a `Content-Type` header (e.g. using `fetch()` with a `Blob` body)
|
||||
* and don't send any authentication credentials.
|
||||
|
||||
This type of attack is mainly relevant when:
|
||||
|
||||
* the application is running locally (e.g. on `localhost`) or in an internal network
|
||||
* and the application doesn't have any authentication, it expects that any request from the same network can be trusted.
|
||||
|
||||
## Example Attack { #example-attack }
|
||||
|
||||
Imagine you build a way to run a local AI agent.
|
||||
|
||||
It provides an API at
|
||||
|
||||
```
|
||||
http://localhost:8000/v1/agents/multivac
|
||||
```
|
||||
|
||||
There's also a frontend at
|
||||
|
||||
```
|
||||
http://localhost:8000
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
Note that both have the same host.
|
||||
|
||||
///
|
||||
|
||||
Then using the frontend you can make the AI agent do things on your behalf.
|
||||
|
||||
As it's running **locally**, and not in the open internet, you decide to **not have any authentication** set up, just trusting the access to the local network.
|
||||
|
||||
Then one of your users could install it and run it locally.
|
||||
|
||||
Then they could open a malicious website, e.g. something like
|
||||
|
||||
```
|
||||
https://evilhackers.example.com
|
||||
```
|
||||
|
||||
And that malicious website sends requests using `fetch()` with a `Blob` body to the local API at
|
||||
|
||||
```
|
||||
http://localhost:8000/v1/agents/multivac
|
||||
```
|
||||
|
||||
Even though the host of the malicious website and the local app is different, the browser won't trigger a CORS preflight request because:
|
||||
|
||||
* It's running without any authentication, it doesn't have to send any credentials.
|
||||
* The browser thinks it's not sending JSON (because of the missing `Content-Type` header).
|
||||
|
||||
Then the malicious website could make the local AI agent send angry messages to the user's ex-boss... or worse. 😅
|
||||
|
||||
## Open Internet { #open-internet }
|
||||
|
||||
If your app is in the open internet, you wouldn't "trust the network" and let anyone send privileged requests without authentication.
|
||||
|
||||
Attackers could simply run a script to send requests to your API, no need for browser interaction, so you are probably already securing any privileged endpoints.
|
||||
|
||||
In that case **this attack / risk doesn't apply to you**.
|
||||
|
||||
This risk and attack is mainly relevant when the app runs on the **local network** and that is the **only assumed protection**.
|
||||
|
||||
## Allowing Requests Without Content-Type { #allowing-requests-without-content-type }
|
||||
|
||||
If you need to support clients that don't send a `Content-Type` header, you can disable strict checking by setting `strict_content_type=False`:
|
||||
|
||||
{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *}
|
||||
|
||||
With this setting, requests without a `Content-Type` header will have their body parsed as JSON, which is the same behavior as older versions of FastAPI.
|
||||
|
||||
/// info
|
||||
|
||||
This behavior and configuration was added in FastAPI 0.132.0.
|
||||
|
||||
///
|
||||
|
|
@ -61,6 +61,10 @@ a.internal-link::after {
|
|||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.md-footer-meta .md-social {
|
||||
padding-right: 4rem;
|
||||
}
|
||||
|
||||
.user-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ Here are several pointers to other places in the docs, for general or frequent q
|
|||
|
||||
To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
|
||||
|
||||
## Optimize Response Performance - Response Model - Return Type { #optimize-response-performance-response-model-return-type }
|
||||
|
||||
To optimize performance when returning JSON data, use a return type or response model, that way Pydantic will handle the serialization to JSON on the Rust side, without going through Python. Read more in the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}.
|
||||
|
||||
## Documentation Tags - OpenAPI { #documentation-tags-openapi }
|
||||
|
||||
To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}.
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
|
|
@ -0,0 +1,29 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var script = document.createElement("script");
|
||||
script.src = "https://widget.kapa.ai/kapa-widget.bundle.js";
|
||||
script.setAttribute("data-website-id", "91f47f27-b405-4299-bf5f-a1c0ec07b3cc");
|
||||
script.setAttribute("data-project-name", "FastAPI");
|
||||
script.setAttribute("data-project-color", "#009485");
|
||||
script.setAttribute("data-project-logo", "https://fastapi.tiangolo.com/img/favicon.png");
|
||||
script.setAttribute("data-bot-protection-mechanism", "hcaptcha");
|
||||
script.setAttribute("data-button-height", "3rem");
|
||||
script.setAttribute("data-button-width", "3rem");
|
||||
script.setAttribute("data-button-border-radius", "50%");
|
||||
script.setAttribute("data-button-padding", "0");
|
||||
script.setAttribute("data-button-image", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E");
|
||||
script.setAttribute("data-button-image-height", "20px");
|
||||
script.setAttribute("data-button-image-width", "20px");
|
||||
script.setAttribute("data-button-text", "Ask AI");
|
||||
script.setAttribute("data-button-text-font-size", "0.5rem");
|
||||
script.setAttribute("data-button-text-font-family", "Roboto, sans-serif");
|
||||
script.setAttribute("data-button-text-color", "#FFFFFF");
|
||||
script.setAttribute("data-modal-border-radius", "0.5rem");
|
||||
script.setAttribute("data-modal-header-bg-color", "#009485");
|
||||
script.setAttribute("data-modal-title", "FastAPI AI Assistant");
|
||||
script.setAttribute("data-modal-title-color", "#FFFFFF");
|
||||
script.setAttribute("data-modal-title-font-family", "Roboto, sans-serif");
|
||||
script.setAttribute("data-modal-example-questions", "How to define a route?,How to validate models?,How to handle responses?,How to deploy FastAPI?");
|
||||
script.setAttribute("data-modal-disclaimer", "AI-generated answers based on FastAPI [documentation](https://fastapi.tiangolo.com/) and [community discussions](https://github.com/fastapi/fastapi/discussions). Always verify important information.");
|
||||
script.async = true;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
|
|
@ -22,7 +22,13 @@ from fastapi.responses import (
|
|||
|
||||
## FastAPI Responses
|
||||
|
||||
There are a couple of custom FastAPI response classes, you can use them to optimize JSON performance.
|
||||
There were a couple of custom FastAPI response classes that were intended to optimize JSON performance.
|
||||
|
||||
However, they are now deprecated as you will now get better performance by using a [Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/).
|
||||
|
||||
That way, Pydantic will serialize the data into JSON bytes on the Rust side, which will achieve better performance than these custom JSON responses.
|
||||
|
||||
Read more about it in [Custom Response - HTML, Stream, File, others - `orjson` or Response Model](https://fastapi.tiangolo.com/advanced/custom-response/#orjson-or-response-model).
|
||||
|
||||
::: fastapi.responses.UJSONResponse
|
||||
options:
|
||||
|
|
|
|||
|
|
@ -7,12 +7,66 @@ hide:
|
|||
|
||||
## Latest Changes
|
||||
|
||||
### Internal
|
||||
|
||||
* 👥 Update FastAPI People - Experts. PR [#14972](https://github.com/fastapi/fastapi/pull/14972) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Allow skipping `benchmark` job in `test` workflow. PR [#14974](https://github.com/fastapi/fastapi/pull/14974) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
||||
## 0.132.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* 🔒️ Add `strict_content_type` checking for JSON requests. PR [#14978](https://github.com/fastapi/fastapi/pull/14978) by [@tiangolo](https://github.com/tiangolo).
|
||||
* Now FastAPI checks, by default, that JSON requests have a `Content-Type` header with a valid JSON value, like `application/json`, and rejects requests that don't.
|
||||
* If the clients for your app don't send a valid `Content-Type` header you can disable this with `strict_content_type=False`.
|
||||
* Check the new docs: [Strict Content-Type Checking](https://fastapi.tiangolo.com/advanced/strict-content-type/).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ Bump flask from 3.1.2 to 3.1.3. PR [#14949](https://github.com/fastapi/fastapi/pull/14949) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Update all dependencies to use `griffelib` instead of `griffe`. PR [#14973](https://github.com/fastapi/fastapi/pull/14973) by [@svlandeg](https://github.com/svlandeg).
|
||||
* 🔨 Fix `FastAPI People` workflow. PR [#14951](https://github.com/fastapi/fastapi/pull/14951) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 👷 Do not run codspeed with coverage as it's not tracked. PR [#14966](https://github.com/fastapi/fastapi/pull/14966) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Do not include benchmark tests in coverage to speed up coverage processing. PR [#14965](https://github.com/fastapi/fastapi/pull/14965) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.131.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* 🗑️ Deprecate `ORJSONResponse` and `UJSONResponse`. PR [#14964](https://github.com/fastapi/fastapi/pull/14964) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.130.0
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ Serialize JSON response with Pydantic (in Rust), when there's a Pydantic return type or response model. PR [#14962](https://github.com/fastapi/fastapi/pull/14962) by [@tiangolo](https://github.com/tiangolo).
|
||||
* This results in 2x (or more) performance increase for JSON responses.
|
||||
* New docs: [Custom Response - JSON Performance](https://fastapi.tiangolo.com/advanced/custom-response/#json-performance).
|
||||
|
||||
## 0.129.2
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆️ Upgrade pytest. PR [#14959](https://github.com/fastapi/fastapi/pull/14959) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Fix CI, do not attempt to publish `fastapi-slim`. PR [#14958](https://github.com/fastapi/fastapi/pull/14958) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ➖ Drop support for `fastapi-slim`, no more versions will be released, use only `"fastapi[standard]"` or `fastapi`. PR [#14957](https://github.com/fastapi/fastapi/pull/14957) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔧 Update pyproject.toml, remove unneeded lines. PR [#14956](https://github.com/fastapi/fastapi/pull/14956) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.129.1
|
||||
|
||||
### Fixes
|
||||
|
||||
* ♻️ Fix JSON Schema for bytes, use `"contentMediaType": "application/octet-stream"` instead of `"format": "binary"`. PR [#14953](https://github.com/fastapi/fastapi/pull/14953) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Docs
|
||||
|
||||
* 🔨 Add Kapa.ai widget (AI chatbot). PR [#14938](https://github.com/fastapi/fastapi/pull/14938) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 🔥 Remove Python 3.9 specific files, no longer needed after updating translations. PR [#14931](https://github.com/fastapi/fastapi/pull/14931) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 📝 Update docs for JWT to prevent timing attacks. PR [#14908](https://github.com/fastapi/fastapi/pull/14908) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
### Translations
|
||||
|
||||
* ✏️ Fix several typos in ru translations. PR [#14934](https://github.com/fastapi/fastapi/pull/14934) by [@argoarsiks](https://github.com/argoarsiks).
|
||||
* 🌐 Update translations for ko (update-all and add-missing). PR [#14923](https://github.com/fastapi/fastapi/pull/14923) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for uk (add-missing). PR [#14922](https://github.com/fastapi/fastapi/pull/14922) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🌐 Update translations for zh-hant (update-all and add-missing). PR [#14921](https://github.com/fastapi/fastapi/pull/14921) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
|
|
@ -28,6 +82,8 @@ hide:
|
|||
|
||||
### Internal
|
||||
|
||||
* 👷 Always run tests on push to `master` branch and when run by scheduler. PR [#14940](https://github.com/fastapi/fastapi/pull/14940) by [@YuriiMotov](https://github.com/YuriiMotov).
|
||||
* 🎨 Upgrade typing syntax for Python 3.10. PR [#14932](https://github.com/fastapi/fastapi/pull/14932) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump cryptography from 46.0.4 to 46.0.5. PR [#14892](https://github.com/fastapi/fastapi/pull/14892) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 12.1.0 to 12.1.1. PR [#14899](https://github.com/fastapi/fastapi/pull/14899) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ FastAPI will use this return type to:
|
|||
* Add a **JSON Schema** for the response, in the OpenAPI *path operation*.
|
||||
* This will be used by the **automatic docs**.
|
||||
* It will also be used by automatic client code generation tools.
|
||||
* **Serialize** the returned data to JSON using Pydantic, which is written in **Rust**, so it will be **much faster**.
|
||||
|
||||
But most importantly:
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,8 @@ nav:
|
|||
- advanced/wsgi.md
|
||||
- advanced/generate-clients.md
|
||||
- advanced/advanced-python-types.md
|
||||
- advanced/json-base64-bytes.md
|
||||
- advanced/strict-content-type.md
|
||||
- fastapi-cli.md
|
||||
- Deployment:
|
||||
- deployment/index.md
|
||||
|
|
@ -342,5 +344,6 @@ extra_css:
|
|||
extra_javascript:
|
||||
- js/termynal.js
|
||||
- js/custom.js
|
||||
- js/init_kapa_widget.js
|
||||
hooks:
|
||||
- ../../scripts/mkdocs_hooks.py
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
|||
Поддерживаются следующие аргументы:
|
||||
|
||||
- `minimum_size` — не сжимать GZip‑ом ответы, размер которых меньше этого минимального значения в байтах. По умолчанию — `500`.
|
||||
- `compresslevel` — уровень GZip‑сжатия. Целое число от 1 до 9. По умолчанию — `9`. Более низкое значение — быстреее сжатие, но больший размер файла; более высокое значение — более медленное сжатие, но меньший размер файла.
|
||||
- `compresslevel` — уровень GZip‑сжатия. Целое число от 1 до 9. По умолчанию — `9`. Более низкое значение — быстрее сжатие, но больший размер файла; более высокое значение — более медленное сжатие, но меньший размер файла.
|
||||
|
||||
## Другие middleware { #other-middlewares }
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ CMD ["fastapi", "run", "app/main.py", "--port", "80"]
|
|||
|
||||
5. Копируем директорию `./app` внутрь директории `/code`.
|
||||
|
||||
Так как здесь весь код, который **меняется чаще всего**, кэш Docker **вряд ли** будет использоваться для этого шагa или **последующих шагов**.
|
||||
Так как здесь весь код, который **меняется чаще всего**, кэш Docker **вряд ли** будет использоваться для этого шага или **последующих шагов**.
|
||||
|
||||
Поэтому важно разместить этот шаг **ближе к концу** `Dockerfile`, чтобы оптимизировать время сборки образа контейнера.
|
||||
|
||||
|
|
|
|||
|
|
@ -76,4 +76,4 @@
|
|||
|
||||
У **FastAPI** великое будущее.
|
||||
|
||||
И [ваш вклад в это](help-fastapi.md){.internal-link target=_blank} - очень ценнен.
|
||||
И [ваш вклад в это](help-fastapi.md){.internal-link target=_blank} - очень ценен.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4
|
|||
|
||||
Но он подписан. Следовательно, когда вы получаете токен, который вы эмитировали (выдавали), вы можете убедиться, что это именно вы его эмитировали.
|
||||
|
||||
Таким образом, можно создать токен со сроком действия, скажем, 1 неделя. А когда пользователь вернется на следующий день с тем же токеном, вы будете знать, что он все еще авторизирован в вашей системе.
|
||||
Таким образом, можно создать токен со сроком действия, скажем, 1 неделя. А когда пользователь вернется на следующий день с тем же токеном, вы будете знать, что он все еще авторизован в вашей системе.
|
||||
|
||||
Через неделю срок действия токена истечет, пользователь не будет авторизован и ему придется заново входить в систему, чтобы получить новый токен. А если пользователь (или третье лицо) попытается модифицировать токен, чтобы изменить срок действия, вы сможете это обнаружить, поскольку подписи не будут совпадать.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={
|
||||
404: {"model": Message, "description": "The item was not found"},
|
||||
200: {
|
||||
"description": "Item requested by ID",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {"id": "bar", "value": "The bar tenders"}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
else:
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(HTTPSRedirectMiddleware)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=5)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return "somebigcontent"
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from fastapi.testclient import TestClient
|
||||
|
||||
from .main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from fastapi.websockets import WebSocket
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"msg": "Hello WebSocket"})
|
||||
await websocket.close()
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
|
||||
|
||||
def test_websocket():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/ws") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"msg": "Hello WebSocket"}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {}
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
items["foo"] = {"name": "Fighters"}
|
||||
items["bar"] = {"name": "Tenders"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
def test_read_items():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
items = {}
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
items["foo"] = {"name": "Fighters"}
|
||||
items["bar"] = {"name": "Tenders"}
|
||||
yield
|
||||
# clean up items
|
||||
items.clear()
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
def test_read_items():
|
||||
# Before the lifespan starts, "items" is still empty
|
||||
assert items == {}
|
||||
|
||||
with TestClient(app) as client:
|
||||
# Inside the "with TestClient" block, the lifespan starts and items added
|
||||
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
|
||||
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
|
||||
# After the requests is done, the items are still there
|
||||
assert items == {"foo": {"name": "Fighters"}, "bar": {"name": "Tenders"}}
|
||||
|
||||
# The end of the "with TestClient" block simulates terminating the app, so
|
||||
# the lifespan ends and items are cleaned up
|
||||
assert items == {}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Tomato"}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from .main import app
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_root():
|
||||
async with AsyncClient(
|
||||
transport=ASGITransport(app=app), base_url="http://test"
|
||||
) as ac:
|
||||
response = await ac.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "Tomato"}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class HTTPBearer403(HTTPBearer):
|
||||
def make_not_authenticated_error(self) -> HTTPException:
|
||||
return HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated"
|
||||
)
|
||||
|
||||
|
||||
CredentialsDep = Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer403())]
|
||||
|
||||
|
||||
@app.get("/me")
|
||||
def read_me(credentials: CredentialsDep):
|
||||
return {"message": "You are authenticated", "token": credentials.credentials}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
from fastapi import BackgroundTasks, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def write_notification(email: str, message=""):
|
||||
with open("log.txt", mode="w") as email_file:
|
||||
content = f"notification for {email}: {message}"
|
||||
email_file.write(content)
|
||||
|
||||
|
||||
@app.post("/send-notification/{email}")
|
||||
async def send_notification(email: str, background_tasks: BackgroundTasks):
|
||||
background_tasks.add_task(write_notification, email, message="some notification")
|
||||
return {"message": "Notification sent in the background"}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items():
|
||||
return ["plumbus", "portal gun"]
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/app")
|
||||
def read_main(request: Request):
|
||||
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI(root_path="/api/v1")
|
||||
|
||||
|
||||
@app.get("/app")
|
||||
def read_main(request: Request):
|
||||
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI(
|
||||
servers=[
|
||||
{"url": "https://stag.example.com", "description": "Staging environment"},
|
||||
{"url": "https://prod.example.com", "description": "Production environment"},
|
||||
],
|
||||
root_path="/api/v1",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/app")
|
||||
def read_main(request: Request):
|
||||
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
from fastapi import FastAPI, Request
|
||||
|
||||
app = FastAPI(
|
||||
servers=[
|
||||
{"url": "https://stag.example.com", "description": "Staging environment"},
|
||||
{"url": "https://prod.example.com", "description": "Production environment"},
|
||||
],
|
||||
root_path="/api/v1",
|
||||
root_path_in_servers=False,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/app")
|
||||
def read_main(request: Request):
|
||||
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Header, HTTPException
|
||||
|
||||
|
||||
async def get_token_header(x_token: Annotated[str, Header()]):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def get_query_token(token: str):
|
||||
if token != "jessica":
|
||||
raise HTTPException(status_code=400, detail="No Jessica token provided")
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/")
|
||||
async def update_admin():
|
||||
return {"message": "Admin getting schwifty"}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
from .dependencies import get_query_token, get_token_header
|
||||
from .internal import admin
|
||||
from .routers import items, users
|
||||
|
||||
app = FastAPI(dependencies=[Depends(get_query_token)])
|
||||
|
||||
|
||||
app.include_router(users.router)
|
||||
app.include_router(items.router)
|
||||
app.include_router(
|
||||
admin.router,
|
||||
prefix="/admin",
|
||||
tags=["admin"],
|
||||
dependencies=[Depends(get_token_header)],
|
||||
responses={418: {"description": "I'm a teapot"}},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Hello Bigger Applications!"}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from ..dependencies import get_token_header
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/items",
|
||||
tags=["items"],
|
||||
dependencies=[Depends(get_token_header)],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
|
||||
fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def read_items():
|
||||
return fake_items_db
|
||||
|
||||
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
if item_id not in fake_items_db:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{item_id}",
|
||||
tags=["custom"],
|
||||
responses={403: {"description": "Operation forbidden"}},
|
||||
)
|
||||
async def update_item(item_id: str):
|
||||
if item_id != "plumbus":
|
||||
raise HTTPException(
|
||||
status_code=403, detail="You can only update the item: plumbus"
|
||||
)
|
||||
return {"item_id": item_id, "name": "The great Plumbus"}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/users/", tags=["users"])
|
||||
async def read_users():
|
||||
return [{"username": "Rick"}, {"username": "Morty"}]
|
||||
|
||||
|
||||
@router.get("/users/me", tags=["users"])
|
||||
async def read_user_me():
|
||||
return {"username": "fakecurrentuser"}
|
||||
|
||||
|
||||
@router.get("/users/{username}", tags=["users"])
|
||||
async def read_user(username: str):
|
||||
return {"username": username}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from fastapi import Header, HTTPException
|
||||
|
||||
|
||||
async def get_token_header(x_token: str = Header()):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def get_query_token(token: str):
|
||||
if token != "jessica":
|
||||
raise HTTPException(status_code=400, detail="No Jessica token provided")
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
from .dependencies import get_query_token, get_token_header
|
||||
from .internal import admin
|
||||
from .routers import items, users
|
||||
|
||||
app = FastAPI(dependencies=[Depends(get_query_token)])
|
||||
|
||||
|
||||
app.include_router(users.router)
|
||||
app.include_router(items.router)
|
||||
app.include_router(
|
||||
admin.router,
|
||||
prefix="/admin",
|
||||
tags=["admin"],
|
||||
dependencies=[Depends(get_token_header)],
|
||||
responses={418: {"description": "I'm a teapot"}},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Hello Bigger Applications!"}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
url: HttpUrl
|
||||
name: str
|
||||
|
||||
|
||||
@app.post("/images/multiple/")
|
||||
async def create_multiple_images(images: list[Image]):
|
||||
return images
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/index-weights/")
|
||||
async def create_index_weights(weights: dict[int, float]):
|
||||
return weights
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
openapi_url: str = "/openapi.json"
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
app = FastAPI(openapi_url=settings.openapi_url)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
return {"message": "Hello World"}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(swagger_ui_parameters={"syntaxHighlight": False})
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}})
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(swagger_ui_parameters={"deepLinking": False})
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
origins = [
|
||||
"http://localhost.tiangolo.com",
|
||||
"https://localhost.tiangolo.com",
|
||||
"http://localhost",
|
||||
"http://localhost:8080",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_swagger_ui_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
async def custom_swagger_ui_html():
|
||||
return get_swagger_ui_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - Swagger UI",
|
||||
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
||||
swagger_js_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js",
|
||||
swagger_css_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css",
|
||||
)
|
||||
|
||||
|
||||
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
|
||||
async def swagger_ui_redirect():
|
||||
return get_swagger_ui_oauth2_redirect_html()
|
||||
|
||||
|
||||
@app.get("/redoc", include_in_schema=False)
|
||||
async def redoc_html():
|
||||
return get_redoc_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - ReDoc",
|
||||
redoc_js_url="https://unpkg.com/redoc@2/bundles/redoc.standalone.js",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_swagger_ui_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
async def custom_swagger_ui_html():
|
||||
return get_swagger_ui_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - Swagger UI",
|
||||
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
||||
swagger_js_url="/static/swagger-ui-bundle.js",
|
||||
swagger_css_url="/static/swagger-ui.css",
|
||||
)
|
||||
|
||||
|
||||
@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
|
||||
async def swagger_ui_redirect():
|
||||
return get_swagger_ui_oauth2_redirect_html()
|
||||
|
||||
|
||||
@app.get("/redoc", include_in_schema=False)
|
||||
async def redoc_html():
|
||||
return get_redoc_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - ReDoc",
|
||||
redoc_js_url="/static/redoc.standalone.js",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"message": f"Hello {username}"}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import UJSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", response_class=UJSONResponse)
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import ORJSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", response_class=ORJSONResponse)
|
||||
async def read_items():
|
||||
return ORJSONResponse([{"item_id": "Foo"}])
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return """
|
||||
<html>
|
||||
<head>
|
||||
<title>Some HTML in here</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Look ma! HTML!</h1>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
html_content = """
|
||||
<html>
|
||||
<head>
|
||||
<title>Some HTML in here</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Look ma! HTML!</h1>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content, status_code=200)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def generate_html_response():
|
||||
html_content = """
|
||||
<html>
|
||||
<head>
|
||||
<title>Some HTML in here</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Look ma! HTML!</h1>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content, status_code=200)
|
||||
|
||||
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return generate_html_response()
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", response_class=PlainTextResponse)
|
||||
async def main():
|
||||
return "Hello World"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/typer")
|
||||
async def redirect_typer():
|
||||
return RedirectResponse("https://typer.tiangolo.com")
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/fastapi", response_class=RedirectResponse)
|
||||
async def redirect_fastapi():
|
||||
return "https://fastapi.tiangolo.com"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
|
||||
async def redirect_pydantic():
|
||||
return "https://docs.pydantic.dev/"
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def fake_video_streamer():
|
||||
for i in range(10):
|
||||
yield b"some fake video bytes"
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return StreamingResponse(fake_video_streamer())
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def main():
|
||||
def iterfile(): # (1)
|
||||
with open(some_file_path, mode="rb") as file_like: # (2)
|
||||
yield from file_like # (3)
|
||||
|
||||
return StreamingResponse(iterfile(), media_type="video/mp4")
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
return FileResponse(some_file_path)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
some_file_path = "large-video-file.mp4"
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/", response_class=FileResponse)
|
||||
async def main():
|
||||
return some_file_path
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from typing import Any
|
||||
|
||||
import orjson
|
||||
from fastapi import FastAPI, Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class CustomORJSONResponse(Response):
|
||||
media_type = "application/json"
|
||||
|
||||
def render(self, content: Any) -> bytes:
|
||||
assert orjson is not None, "orjson must be installed"
|
||||
return orjson.dumps(content, option=orjson.OPT_INDENT_2)
|
||||
|
||||
|
||||
@app.get("/", response_class=CustomORJSONResponse)
|
||||
async def main():
|
||||
return {"message": "Hello World"}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import ORJSONResponse
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI(default_response_class=ORJSONResponse)
|
||||
app = FastAPI(default_response_class=HTMLResponse)
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
return "<h1>Items</h1><p>This is a list of items.</p>"
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.responses import ORJSONResponse
|
||||
|
||||
app = FastAPI(default_response_class=ORJSONResponse)
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
def root():
|
||||
a = "a"
|
||||
b = "b" + a
|
||||
return {"hello world": b}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def verify_token(x_token: Annotated[str, Header()]):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def verify_key(x_key: Annotated[str, Header()]):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
async def read_items():
|
||||
return [{"item": "Foo"}, {"item": "Bar"}]
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def verify_token(x_token: str = Header()):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def verify_key(x_key: str = Header()):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
async def read_items():
|
||||
return [{"item": "Foo"}, {"item": "Bar"}]
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
async def get_db():
|
||||
db = DBSession()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
|
||||
|
||||
async def dependency_a():
|
||||
dep_a = generate_dep_a()
|
||||
try:
|
||||
yield dep_a
|
||||
finally:
|
||||
dep_a.close()
|
||||
|
||||
|
||||
async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):
|
||||
dep_b = generate_dep_b()
|
||||
try:
|
||||
yield dep_b
|
||||
finally:
|
||||
dep_b.close(dep_a)
|
||||
|
||||
|
||||
async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):
|
||||
dep_c = generate_dep_c()
|
||||
try:
|
||||
yield dep_c
|
||||
finally:
|
||||
dep_c.close(dep_b)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
from fastapi import Depends
|
||||
|
||||
|
||||
async def dependency_a():
|
||||
dep_a = generate_dep_a()
|
||||
try:
|
||||
yield dep_a
|
||||
finally:
|
||||
dep_a.close()
|
||||
|
||||
|
||||
async def dependency_b(dep_a=Depends(dependency_a)):
|
||||
dep_b = generate_dep_b()
|
||||
try:
|
||||
yield dep_b
|
||||
finally:
|
||||
dep_b.close(dep_a)
|
||||
|
||||
|
||||
async def dependency_c(dep_b=Depends(dependency_b)):
|
||||
dep_c = generate_dep_c()
|
||||
try:
|
||||
yield dep_c
|
||||
finally:
|
||||
dep_c.close(dep_b)
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
data = {
|
||||
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
|
||||
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
|
||||
}
|
||||
|
||||
|
||||
class OwnerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except OwnerError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
|
||||
if item_id not in data:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
item = data[item_id]
|
||||
if item["owner"] != username:
|
||||
raise OwnerError(username)
|
||||
return item
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
data = {
|
||||
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
|
||||
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
|
||||
}
|
||||
|
||||
|
||||
class OwnerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except OwnerError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Owner error: {e}")
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: str = Depends(get_username)):
|
||||
if item_id not in data:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
item = data[item_id]
|
||||
if item["owner"] != username:
|
||||
raise OwnerError(username)
|
||||
return item
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except InternalError:
|
||||
print("Oops, we didn't raise again, Britney 😱")
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
|
||||
if item_id == "portal-gun":
|
||||
raise InternalError(
|
||||
f"The portal gun is too dangerous to be owned by {username}"
|
||||
)
|
||||
if item_id != "plumbus":
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Item not found, there's only a plumbus here"
|
||||
)
|
||||
return item_id
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except InternalError:
|
||||
print("Oops, we didn't raise again, Britney 😱")
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: str = Depends(get_username)):
|
||||
if item_id == "portal-gun":
|
||||
raise InternalError(
|
||||
f"The portal gun is too dangerous to be owned by {username}"
|
||||
)
|
||||
if item_id != "plumbus":
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Item not found, there's only a plumbus here"
|
||||
)
|
||||
return item_id
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except InternalError:
|
||||
print("We don't swallow the internal error here, we raise again 😎")
|
||||
raise
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
|
||||
if item_id == "portal-gun":
|
||||
raise InternalError(
|
||||
f"The portal gun is too dangerous to be owned by {username}"
|
||||
)
|
||||
if item_id != "plumbus":
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Item not found, there's only a plumbus here"
|
||||
)
|
||||
return item_id
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from fastapi import Depends, FastAPI, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
except InternalError:
|
||||
print("We don't swallow the internal error here, we raise again 😎")
|
||||
raise
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def get_item(item_id: str, username: str = Depends(get_username)):
|
||||
if item_id == "portal-gun":
|
||||
raise InternalError(
|
||||
f"The portal gun is too dangerous to be owned by {username}"
|
||||
)
|
||||
if item_id != "plumbus":
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Item not found, there's only a plumbus here"
|
||||
)
|
||||
return item_id
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
finally:
|
||||
print("Cleanup up before response is sent")
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def get_user_me(username: Annotated[str, Depends(get_username, scope="function")]):
|
||||
return username
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def get_username():
|
||||
try:
|
||||
yield "Rick"
|
||||
finally:
|
||||
print("Cleanup up before response is sent")
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def get_user_me(username: str = Depends(get_username, scope="function")):
|
||||
return username
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
class MySuperContextManager:
|
||||
def __init__(self):
|
||||
self.db = DBSession()
|
||||
|
||||
def __enter__(self):
|
||||
return self.db
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.db.close()
|
||||
|
||||
|
||||
async def get_db():
|
||||
with MySuperContextManager() as db:
|
||||
yield db
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: Annotated[bool, Depends(checker)]):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
|
||||
async def verify_token(x_token: Annotated[str, Header()]):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def verify_key(x_key: Annotated[str, Header()]):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
|
||||
|
||||
|
||||
@app.get("/users/")
|
||||
async def read_users():
|
||||
return [{"username": "Rick"}, {"username": "Morty"}]
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
|
||||
async def verify_token(x_token: str = Header()):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
async def verify_key(x_key: str = Header()):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
|
||||
|
||||
|
||||
@app.get("/users/")
|
||||
async def read_users():
|
||||
return [{"username": "Rick"}, {"username": "Morty"}]
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {}
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
items["foo"] = {"name": "Fighters"}
|
||||
items["bar"] = {"name": "Tenders"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
def shutdown_event():
|
||||
with open("log.txt", mode="a") as log:
|
||||
log.write("Application shutdown")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"name": "Foo"}]
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
def fake_answer_to_everything_ml_model(x: float):
|
||||
return x * 42
|
||||
|
||||
|
||||
ml_models = {}
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Load the ML model
|
||||
ml_models["answer_to_everything"] = fake_answer_to_everything_ml_model
|
||||
yield
|
||||
# Clean up the ML models and release the resources
|
||||
ml_models.clear()
|
||||
|
||||
|
||||
app = FastAPI(lifespan=lifespan)
|
||||
|
||||
|
||||
@app.get("/predict")
|
||||
async def predict(x: float):
|
||||
result = ml_models["answer_to_everything"](x)
|
||||
return {"result": result}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"name": "Foo"}]
|
||||
|
||||
|
||||
def custom_openapi():
|
||||
if app.openapi_schema:
|
||||
return app.openapi_schema
|
||||
openapi_schema = get_openapi(
|
||||
title="Custom title",
|
||||
version="2.5.0",
|
||||
summary="This is a very custom OpenAPI schema",
|
||||
description="Here's a longer description of the custom **OpenAPI** schema",
|
||||
routes=app.routes,
|
||||
)
|
||||
openapi_schema["info"]["x-logo"] = {
|
||||
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
|
||||
}
|
||||
app.openapi_schema = openapi_schema
|
||||
return app.openapi_schema
|
||||
|
||||
|
||||
app.openapi = custom_openapi
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
from typing import Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
@ -30,6 +28,6 @@ items = {
|
|||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
|
||||
@app.get("/items/{item_id}", response_model=PlaneItem | CarItem)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
items = [
|
||||
{"name": "Foo", "description": "There comes my hero"},
|
||||
{"name": "Red", "description": "It's my aeroplane"},
|
||||
]
|
||||
|
||||
|
||||
@app.get("/items/", response_model=list[Item])
|
||||
async def read_items():
|
||||
return items
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/keyword-weights/", response_model=dict[str, float])
|
||||
async def read_keyword_weights():
|
||||
return {"foo": 2.3, "bar": 3.4}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue