mirror of https://github.com/tiangolo/fastapi.git
✨ Re-export utils from Starlette (#1064)
* ✨ Re-export main features used from Starlette to simplify developer's code * ♻️ Refactor Starlette exports * ♻️ Refactor tutorial examples to use re-exported utils from Starlette * 📝 Add examples for all middlewares * 📝 Add new docs for middlewares * 📝 Add examples for custom responses * 📝 Extend docs for custom responses * 📝 Update docs and add notes explaining re-exports from Starlette everywhere * 🍱 Update screenshot for HTTP status * 🔧 Update MkDocs config with new content * ♻️ Refactor tests to use re-exported utils from Starlette * ✨ Re-export WebSocketDisconnect from Starlette for tests * ✅ Add extra tests for extra re-exported middleware * ✅ Add tests for re-exported responses from Starlette * ✨ Add docs about mounting WSGI apps * ➕ Add Flask as a dependency to test WSGIMiddleware * ✅ Test WSGIMiddleware example
This commit is contained in:
parent
f2bd2c44e2
commit
0ac9b3ee5c
|
|
@ -1,4 +1,4 @@
|
||||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
|
By default, **FastAPI** will return the responses using a `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
|
||||||
|
|
||||||
It will use the default status code or the one you set in your *path operation*.
|
It will use the default status code or the one you set in your *path operation*.
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ But you also want it to accept new items. And when the items didn't exist before
|
||||||
|
|
||||||
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
|
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
|
||||||
|
|
||||||
```Python hl_lines="2 20"
|
```Python hl_lines="2 19"
|
||||||
{!./src/additional_status_codes/tutorial001.py!}
|
{!./src/additional_status_codes/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -23,8 +23,13 @@ To achieve that, import `JSONResponse`, and return your content there directly,
|
||||||
|
|
||||||
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
|
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import JSONResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `status`.
|
||||||
|
|
||||||
## OpenAPI and API docs
|
## OpenAPI and API docs
|
||||||
|
|
||||||
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know before hand what you are going to return.
|
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know beforehand what you are going to return.
|
||||||
|
|
||||||
But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}.
|
But you can document that in your code, using: [Additional Responses](additional-responses.md){.internal-link target=_blank}.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
!!! warning
|
|
||||||
This is, more or less, an "advanced" chapter.
|
|
||||||
|
|
||||||
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
|
|
||||||
|
|
||||||
## Parameterized dependencies
|
## Parameterized dependencies
|
||||||
|
|
||||||
All the dependencies we have seen are a fixed function or class.
|
All the dependencies we have seen are a fixed function or class.
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,16 @@ And an `APIRoute` subclass to use that custom request class.
|
||||||
|
|
||||||
### Create a custom `GzipRequest` class
|
### Create a custom `GzipRequest` class
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
This is a toy example to demonstrate how it works, if you need Gzip support, you can use the provided [`GzipMiddleware`](./middleware.md#gzipmiddleware){.internal-link target=_blank}.
|
||||||
|
|
||||||
First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header.
|
First, we create a `GzipRequest` class, which will overwrite the `Request.body()` method to decompress the body in the presence of an appropriate header.
|
||||||
|
|
||||||
If there's no `gzip` in the header, it will not try to decompress the body.
|
If there's no `gzip` in the header, it will not try to decompress the body.
|
||||||
|
|
||||||
That way, the same route class can handle gzip compressed or uncompressed requests.
|
That way, the same route class can handle gzip compressed or uncompressed requests.
|
||||||
|
|
||||||
```Python hl_lines="10 11 12 13 14 15 16 17"
|
```Python hl_lines="8 9 10 11 12 13 14 15"
|
||||||
{!./src/custom_request_and_route/tutorial001.py!}
|
{!./src/custom_request_and_route/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -45,7 +48,7 @@ This method returns a function. And that function is what will receive a request
|
||||||
|
|
||||||
Here we use it to create a `GzipRequest` from the original request.
|
Here we use it to create a `GzipRequest` from the original request.
|
||||||
|
|
||||||
```Python hl_lines="20 21 22 23 24 25 26 27 28"
|
```Python hl_lines="18 19 20 21 22 23 24 25 26"
|
||||||
{!./src/custom_request_and_route/tutorial001.py!}
|
{!./src/custom_request_and_route/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -79,13 +82,13 @@ We can also use this same approach to access the request body in an exception ha
|
||||||
|
|
||||||
All we need to do is handle the request inside a `try`/`except` block:
|
All we need to do is handle the request inside a `try`/`except` block:
|
||||||
|
|
||||||
```Python hl_lines="15 17"
|
```Python hl_lines="13 15"
|
||||||
{!./src/custom_request_and_route/tutorial002.py!}
|
{!./src/custom_request_and_route/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
|
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
|
||||||
|
|
||||||
```Python hl_lines="18 19 20"
|
```Python hl_lines="16 17 18"
|
||||||
{!./src/custom_request_and_route/tutorial002.py!}
|
{!./src/custom_request_and_route/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -93,12 +96,12 @@ If an exception occurs, the`Request` instance will still be in scope, so we can
|
||||||
|
|
||||||
You can also set the `route_class` parameter of an `APIRouter`:
|
You can also set the `route_class` parameter of an `APIRouter`:
|
||||||
|
|
||||||
```Python hl_lines="28"
|
```Python hl_lines="26"
|
||||||
{!./src/custom_request_and_route/tutorial003.py!}
|
{!./src/custom_request_and_route/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
|
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
|
||||||
|
|
||||||
```Python hl_lines="15 16 17 18 19 20 21 22"
|
```Python hl_lines="13 14 15 16 17 18 19 20"
|
||||||
{!./src/custom_request_and_route/tutorial003.py!}
|
{!./src/custom_request_and_route/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
!!! warning
|
By default, **FastAPI** will return the responses using `JSONResponse`.
|
||||||
This is a rather advanced topic.
|
|
||||||
|
|
||||||
If you are starting with **FastAPI**, you might not need this.
|
|
||||||
|
|
||||||
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`.
|
|
||||||
|
|
||||||
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
|
You can override it by returning a `Response` directly as seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}.
|
||||||
|
|
||||||
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type`).
|
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type` as part of the generated OpenAPI).
|
||||||
|
|
||||||
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
|
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
|
||||||
|
|
||||||
|
|
@ -20,7 +15,7 @@ And if that `Response` has a JSON media type (`application/json`), like is the c
|
||||||
|
|
||||||
## Use `UJSONResponse`
|
## Use `UJSONResponse`
|
||||||
|
|
||||||
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be Starlette's `UJSONResponse`.
|
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be `UJSONResponse`.
|
||||||
|
|
||||||
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
|
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
|
||||||
|
|
||||||
|
|
@ -28,9 +23,6 @@ Import the `Response` class (sub-class) you want to use and declare it in the *p
|
||||||
{!./src/custom_response/tutorial001.py!}
|
{!./src/custom_response/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
|
||||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||||
|
|
||||||
|
|
@ -49,9 +41,6 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||||
{!./src/custom_response/tutorial002.py!}
|
{!./src/custom_response/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
|
||||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
The parameter `response_class` will also be used to define the "media type" of the response.
|
The parameter `response_class` will also be used to define the "media type" of the response.
|
||||||
|
|
||||||
|
|
@ -59,7 +48,7 @@ To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
|
||||||
|
|
||||||
And it will be documented as such in OpenAPI.
|
And it will be documented as such in OpenAPI.
|
||||||
|
|
||||||
### Return a Starlette `Response`
|
### Return a `Response`
|
||||||
|
|
||||||
As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, you can also override the response directly in your *path operation*, by returning it.
|
As seen in [Return a Response directly](response-directly.md){.internal-link target=_blank}, you can also override the response directly in your *path operation*, by returning it.
|
||||||
|
|
||||||
|
|
@ -89,14 +78,115 @@ For example, it could be something like:
|
||||||
{!./src/custom_response/tutorial004.py!}
|
{!./src/custom_response/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the function `generate_html_response()` already generates a Starlette `Response` instead of the HTML in a `str`.
|
In this example, the function `generate_html_response()` already generates and returns a `Response` instead of returning the HTML in a `str`.
|
||||||
|
|
||||||
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
|
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
|
||||||
|
|
||||||
But as you passed the `HTMLResponse` in the `response_class`, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
|
But as you passed the `HTMLResponse` in the `response_class` too, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
|
||||||
|
|
||||||
<img src="/img/tutorial/custom-response/image01.png">
|
<img src="/img/tutorial/custom-response/image01.png">
|
||||||
|
|
||||||
|
## Available responses
|
||||||
|
|
||||||
|
Here are some of the available responses.
|
||||||
|
|
||||||
|
Have in mind that you can use `Response` to return anything else, or even create a custom sub-class.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import HTMLResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
|
### `Response`
|
||||||
|
|
||||||
|
The main `Response` class, all the other responses inherit from it.
|
||||||
|
|
||||||
|
You can return it directly.
|
||||||
|
|
||||||
|
It accepts the following parameters:
|
||||||
|
|
||||||
|
* `content` - A `str` or `bytes`.
|
||||||
|
* `status_code` - An `int` HTTP status code.
|
||||||
|
* `headers` - A `dict` of strings.
|
||||||
|
* `media_type` - A `str` giving the media type. E.g. `"text/html"`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```Python hl_lines="1 18"
|
||||||
|
{!./src/response_directly/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `HTMLResponse`
|
||||||
|
|
||||||
|
Takes some text or bytes and returns an HTML response, as you read above.
|
||||||
|
|
||||||
|
### `PlainTextResponse`
|
||||||
|
|
||||||
|
Takes some text or bytes and returns an plain text response.
|
||||||
|
|
||||||
|
```Python hl_lines="2 7 9"
|
||||||
|
{!./src/custom_response/tutorial005.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `JSONResponse`
|
||||||
|
|
||||||
|
Takes some data and returns an `application/json` encoded response.
|
||||||
|
|
||||||
|
This is the default response used in **FastAPI**, as you read above.
|
||||||
|
|
||||||
|
### `UJSONResponse`
|
||||||
|
|
||||||
|
An alternative JSON response using `ujson` for faster serialization as you read above.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
|
||||||
|
|
||||||
|
### `RedirectResponse`
|
||||||
|
|
||||||
|
Returns an HTTP redirect. Uses a 307 status code (Temporary Redirect) by default.
|
||||||
|
|
||||||
|
```Python hl_lines="2 9"
|
||||||
|
{!./src/custom_response/tutorial006.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `StreamingResponse`
|
||||||
|
|
||||||
|
Takes an async generator or a normal generator/iterator and streams the response body.
|
||||||
|
|
||||||
|
```Python hl_lines="2 14"
|
||||||
|
{!./src/custom_response/tutorial007.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Using `StreamingResponse` with file-like objects
|
||||||
|
|
||||||
|
If you have a file-like object (e.g. the object returned by `open()`), you can return it in a `StreamingResponse`.
|
||||||
|
|
||||||
|
This includes many libraries to interact with cloud storage, video processing, and others.
|
||||||
|
|
||||||
|
```Python hl_lines="2 10 11"
|
||||||
|
{!./src/custom_response/tutorial008.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Notice that here as we are using standard `open()` that doesn't support `async` and `await`, we declare the path operation with normal `def`.
|
||||||
|
|
||||||
|
### `FileResponse`
|
||||||
|
|
||||||
|
Asynchronously streams a file as the response.
|
||||||
|
|
||||||
|
Takes a different set of arguments to instantiate than the other response types:
|
||||||
|
|
||||||
|
* `path` - The filepath to the file to stream.
|
||||||
|
* `headers` - Any custom headers to include, as a dictionary.
|
||||||
|
* `media_type` - A string giving the media type. If unset, the filename or path will be used to infer a media type.
|
||||||
|
* `filename` - If set, this will be included in the response `Content-Disposition`.
|
||||||
|
|
||||||
|
File responses will include appropriate `Content-Length`, `Last-Modified` and `ETag` headers.
|
||||||
|
|
||||||
|
```Python hl_lines="2 10"
|
||||||
|
{!./src/custom_response/tutorial009.py!}
|
||||||
|
```
|
||||||
|
|
||||||
## Additional documentation
|
## Additional documentation
|
||||||
|
|
||||||
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
You can also declare the media type and many other details in OpenAPI using `responses`: [Additional Responses in OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ pip install aiofiles
|
||||||
|
|
||||||
### Serve the static files
|
### Serve the static files
|
||||||
|
|
||||||
* Import `StaticFiles` from Starlette.
|
* Import `StaticFiles`.
|
||||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||||
|
|
||||||
```Python hl_lines="7 11"
|
```Python hl_lines="7 11"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
In the main tutorial you read how to add [Custom Middleware](../tutorial/middleware.md){.internal-link target=_blank} to your application.
|
||||||
|
|
||||||
|
And then you also read how to handle [CORS with the `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
|
||||||
|
|
||||||
|
In this section we'll see how to use other middlewares.
|
||||||
|
|
||||||
|
## Adding ASGI middlewares
|
||||||
|
|
||||||
|
As **FastAPI** is based on Starlette and implements the <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr> specification, you can use any ASGI middleware.
|
||||||
|
|
||||||
|
A middleware doesn't have to be made for FastAPI or Starlette to work, as long as it follows the ASGI spec.
|
||||||
|
|
||||||
|
In general, ASGI middlewares are classes that expect to receive an ASGI app as the first argument.
|
||||||
|
|
||||||
|
So, in the documentation for third-party ASGI middlewares they will probably tell you to do something like:
|
||||||
|
|
||||||
|
```Python
|
||||||
|
from unicorn import UnicornMiddleware
|
||||||
|
|
||||||
|
app = SomeASGIApp()
|
||||||
|
|
||||||
|
new_app = UnicornMiddleware(app, some_config="rainbow")
|
||||||
|
```
|
||||||
|
|
||||||
|
But FastAPI (actually Starlette) provides a simpler way to do it that makes sure that the internal middlewares to handle server errors and custom exception handlers work properly.
|
||||||
|
|
||||||
|
For that, you use `app.add_middleware()` (as in the example for CORS).
|
||||||
|
|
||||||
|
```Python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from unicorn import UnicornMiddleware
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||||
|
```
|
||||||
|
|
||||||
|
`app.add_middleware()` receives a middleware class as the first argument and any additional arguments to be passed to the middleware.
|
||||||
|
|
||||||
|
## Integrated middlewares
|
||||||
|
|
||||||
|
**FastAPI** includes several middlewares for common use cases, we'll see next how to use them.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
For the next examples, you could also use `from starlette.middleware.something import SomethingMiddleware`.
|
||||||
|
|
||||||
|
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
|
||||||
|
|
||||||
|
## `HTTPSRedirectMiddleware`
|
||||||
|
|
||||||
|
Enforces that all incoming requests must either be `https` or `wss`.
|
||||||
|
|
||||||
|
Any incoming requests to `http` or `ws` will be redirected to the secure scheme instead.
|
||||||
|
|
||||||
|
```Python hl_lines="2 6"
|
||||||
|
{!./src/advanced_middleware/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `TrustedHostMiddleware`
|
||||||
|
|
||||||
|
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
|
||||||
|
|
||||||
|
```Python hl_lines="2 6 7 8"
|
||||||
|
{!./src/advanced_middleware/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains to allow any hostname either use `allowed_hosts=["*"]` or omit the middleware.
|
||||||
|
|
||||||
|
If an incoming request does not validate correctly then a `400` response will be sent.
|
||||||
|
|
||||||
|
## `GZipMiddleware`
|
||||||
|
|
||||||
|
Handles GZip responses for any request that includes `"gzip"` in the `Accept-Encoding` header.
|
||||||
|
|
||||||
|
The middleware will handle both standard and streaming responses.
|
||||||
|
|
||||||
|
```Python hl_lines="2 6 7 8"
|
||||||
|
{!./src/advanced_middleware/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
|
||||||
|
|
||||||
|
## Other middlewares
|
||||||
|
|
||||||
|
There are many other ASGI middlewares.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
* <a href="https://docs.sentry.io/platforms/python/asgi/" class="external-link" target="_blank">Sentry</a>
|
||||||
|
* <a href="https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py" class="external-link" target="_blank">Uvicorn's `ProxyHeadersMiddleware`</a>
|
||||||
|
* <a href="https://github.com/florimondmanca/msgpack-asgi" class="external-link" target="_blank">MessagePack</a>
|
||||||
|
|
||||||
|
To see other available middlewares check <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">Starlette's Middleware docs</a> and the <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">ASGI Awesome List</a>.
|
||||||
|
|
@ -18,7 +18,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.
|
And then you can set the `status_code` in that *temporal* response object.
|
||||||
|
|
||||||
```Python hl_lines="2 11 14"
|
```Python hl_lines="1 9 12"
|
||||||
{!./src/response_change_status_code/tutorial001.py!}
|
{!./src/response_change_status_code/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
You can declare a parameter of type `Response` in your *path operation function*.
|
You can declare a parameter of type `Response` in your *path operation function*.
|
||||||
|
|
||||||
And then you can set headers in that *temporal* response object.
|
And then you can set cookies in that *temporal* response object.
|
||||||
|
|
||||||
```Python hl_lines="2 8 9"
|
```Python hl_lines="1 8 9"
|
||||||
{!./src/response_cookies/tutorial002.py!}
|
{!./src/response_cookies/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -37,4 +37,11 @@ Then set Cookies in it, and then return it:
|
||||||
|
|
||||||
### More info
|
### More info
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
|
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
|
||||||
|
|
||||||
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">documentation in Starlette</a>.
|
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">documentation in Starlette</a>.
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,20 @@ When you create a **FastAPI** *path operation* you can normally return any data
|
||||||
|
|
||||||
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}.
|
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}.
|
||||||
|
|
||||||
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a Starlette `JSONResponse` that would be used to send the response to the client.
|
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.
|
||||||
|
|
||||||
But you can return a `JSONResponse` directly from your *path operations*.
|
But you can return a `JSONResponse` directly from your *path operations*.
|
||||||
|
|
||||||
It might be useful, for example, to return custom headers or cookies.
|
It might be useful, for example, to return custom headers or cookies.
|
||||||
|
|
||||||
## Starlette `Response`
|
## Return a `Response`
|
||||||
|
|
||||||
In fact, you can return any <a href="https://www.starlette.io/responses/" class="external-link" target="_blank">Starlette `Response`</a> or any sub-class of it.
|
In fact, you can return any `Response` or any sub-class of it.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
`JSONResponse` itself is a sub-class of `Response`.
|
`JSONResponse` itself is a sub-class of `Response`.
|
||||||
|
|
||||||
And when you return a Starlette `Response`, **FastAPI** will pass it directly.
|
And when you return a `Response`, **FastAPI** will pass it directly.
|
||||||
|
|
||||||
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
|
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
|
||||||
|
|
||||||
|
|
@ -33,8 +33,10 @@ For those cases, you can use the `jsonable_encoder` to convert your data before
|
||||||
{!./src/response_directly/tutorial001.py!}
|
{!./src/response_directly/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note "Technical Details"
|
||||||
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
|
You could also use `from starlette.responses import JSONResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
## Returning a custom `Response`
|
## Returning a custom `Response`
|
||||||
|
|
||||||
|
|
@ -42,13 +44,11 @@ The example above shows all the parts you need, but it's not very useful yet, as
|
||||||
|
|
||||||
Now, let's see how you could use that to return a custom response.
|
Now, let's see how you could use that to return a custom response.
|
||||||
|
|
||||||
Let's say you want to return a response that is not available in the default <a href="https://www.starlette.io/responses/" class="external-link" target="_blank">Starlette `Response`s</a>.
|
Let's say that you want to return an <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> response.
|
||||||
|
|
||||||
Let's say that you want to return <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a>.
|
You could put your XML content in a string, put it in a `Response`, and return it:
|
||||||
|
|
||||||
You could put your XML content in a string, put it in a Starlette Response, and return it:
|
```Python hl_lines="1 18"
|
||||||
|
|
||||||
```Python hl_lines="2 20"
|
|
||||||
{!./src/response_directly/tutorial002.py!}
|
{!./src/response_directly/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,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.
|
And then you can set headers in that *temporal* response object.
|
||||||
|
|
||||||
```Python hl_lines="2 8 9"
|
```Python hl_lines="1 7 8"
|
||||||
{!./src/response_headers/tutorial002.py!}
|
{!./src/response_headers/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -26,6 +26,13 @@ Create a response as described in [Return a Response Directly](response-directly
|
||||||
{!./src/response_headers/tutorial001.py!}
|
{!./src/response_headers/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import Response` or `from starlette.responses import JSONResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
|
And as the `Response` can be used frequently to set headers and cookies, **FastAPI** also provides it at `fastapi.Response`.
|
||||||
|
|
||||||
## Custom Headers
|
## Custom Headers
|
||||||
|
|
||||||
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">using the 'X-' prefix</a>.
|
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">using the 'X-' prefix</a>.
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ Use a dependency to check if the username and password are correct.
|
||||||
|
|
||||||
For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password:
|
For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password:
|
||||||
|
|
||||||
```Python hl_lines="1 13 14 15"
|
```Python hl_lines="1 11 12 13"
|
||||||
{!./src/security/tutorial007.py!}
|
{!./src/security/tutorial007.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -100,6 +100,6 @@ 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:
|
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:
|
||||||
|
|
||||||
```Python hl_lines="16 17 18 19 20"
|
```Python hl_lines="15 16 17 18 19"
|
||||||
{!./src/security/tutorial007.py!}
|
{!./src/security/tutorial007.py!}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ They are normally used to declare specific security permissions, for example:
|
||||||
|
|
||||||
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
|
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
|
||||||
|
|
||||||
```Python hl_lines="2 5 9 13 48 66 107 109 110 111 112 113 114 115 116 117 123 124 125 126 130 131 132 133 134 135 136 141 155"
|
```Python hl_lines="2 5 9 13 47 65 106 108 109 110 111 112 113 114 115 116 122 123 124 125 129 130 131 132 133 134 135 140 154"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ The first change is that now we are declaring the OAuth2 security scheme with tw
|
||||||
|
|
||||||
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
|
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
|
||||||
|
|
||||||
```Python hl_lines="64 65 66 67"
|
```Python hl_lines="63 64 65 66"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ And we return the scopes as part of the JWT token.
|
||||||
|
|
||||||
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
|
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
|
||||||
|
|
||||||
```Python hl_lines="156"
|
```Python hl_lines="155"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ In this case, it requires the scope `me` (it could require more than one scope).
|
||||||
|
|
||||||
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
|
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
|
||||||
|
|
||||||
```Python hl_lines="5 141 168"
|
```Python hl_lines="5 140 167"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -141,7 +141,7 @@ We also declare a special parameter of type `SecurityScopes`, imported from `fas
|
||||||
|
|
||||||
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
|
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
|
||||||
|
|
||||||
```Python hl_lines="9 107"
|
```Python hl_lines="9 106"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -157,7 +157,7 @@ We create an `HTTPException` that we can re-use (`raise`) later at several point
|
||||||
|
|
||||||
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in in the `WWW-Authenticate` header (this is part of the spec).
|
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in in the `WWW-Authenticate` header (this is part of the spec).
|
||||||
|
|
||||||
```Python hl_lines="107 109 110 111 112 113 114 115 116 117"
|
```Python hl_lines="106 108 109 110 111 112 113 114 115 116"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -175,7 +175,7 @@ Instead of, for example, a `dict`, or something else, as it could break the appl
|
||||||
|
|
||||||
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
|
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
|
||||||
|
|
||||||
```Python hl_lines="48 118 119 120 121 122 123 124 125 126 127 128 129"
|
```Python hl_lines="47 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ We now verify that all the scopes required, by this dependency and all the depen
|
||||||
|
|
||||||
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
|
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
|
||||||
|
|
||||||
```Python hl_lines="130 131 132 133 134 135 136"
|
```Python hl_lines="129 130 131 132 133 134 135"
|
||||||
{!./src/security/tutorial005.py!}
|
{!./src/security/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ You can use any template engine you want with **FastAPI**.
|
||||||
|
|
||||||
A common election is Jinja2, the same one used by Flask and other tools.
|
A common election is Jinja2, the same one used by Flask and other tools.
|
||||||
|
|
||||||
Starlette has utilities to configure it easily that you can use directly in your **FastAPI** application.
|
There are utilities to configure it easily that you can use directly in your **FastAPI** application (provided by Starlette).
|
||||||
|
|
||||||
## Install dependencies
|
## Install dependencies
|
||||||
|
|
||||||
|
|
@ -20,18 +20,23 @@ pip install aiofiles
|
||||||
|
|
||||||
## Using `Jinja2Templates`
|
## Using `Jinja2Templates`
|
||||||
|
|
||||||
* Import `Jinja2Templates` form Starlette.
|
* Import `Jinja2Templates`.
|
||||||
* Create a `templates` object that you can re-use later.
|
* Create a `templates` object that you can re-use later.
|
||||||
* Declare a `Request` parameter in the *path operation* that will return a template.
|
* Declare a `Request` parameter in the *path operation* that will return a template.
|
||||||
* Use the `templates` you created to render and return a `TemplateResponse`, passing the `request` as one of the key-value pairs in the Jinja2 "context".
|
* Use the `templates` you created to render and return a `TemplateResponse`, passing the `request` as one of the key-value pairs in the Jinja2 "context".
|
||||||
|
|
||||||
```Python hl_lines="4 11 15 16"
|
```Python hl_lines="3 10 14 15"
|
||||||
{!./src/templates/tutorial001.py!}
|
{!./src/templates/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
Notice that you have to pass the `request` as part of the key-value pairs in the context for Jinja2. So, you also have to declare it in your *path operation*.
|
Notice that you have to pass the `request` as part of the key-value pairs in the context for Jinja2. So, you also have to declare it in your *path operation*.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.templating import Jinja2Templates`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.templating` as `fastapi.templating` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request` and `StaticFiles`.
|
||||||
|
|
||||||
## Writing templates
|
## Writing templates
|
||||||
|
|
||||||
Then you can write a template at `templates/item.html` with:
|
Then you can write a template at `templates/item.html` with:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ But there are situations where you might need to access the `Request` object dir
|
||||||
|
|
||||||
As **FastAPI** is actually **Starlette** underneath, with a layer of several tools on top, you can use Starlette's <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> object directly when you need to.
|
As **FastAPI** is actually **Starlette** underneath, with a layer of several tools on top, you can use Starlette's <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> object directly when you need to.
|
||||||
|
|
||||||
It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or annotated (with OpenAPI, for the automatic documentation) by FastAPI.
|
It would also mean that if you get data from the `Request` object directly (for example, read the body) it won't be validated, converted or documented (with OpenAPI, for the automatic API user interface) by FastAPI.
|
||||||
|
|
||||||
Although any other parameter declared normally (for example, the body with a Pydantic model) would still be validated, converted, annotated, etc.
|
Although any other parameter declared normally (for example, the body with a Pydantic model) would still be validated, converted, annotated, etc.
|
||||||
|
|
||||||
|
|
@ -27,24 +27,14 @@ 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.
|
For that you need to access the request directly.
|
||||||
|
|
||||||
### Import the `Request`
|
```Python hl_lines="1 7 8"
|
||||||
|
|
||||||
First, import the `Request` class from Starlette:
|
|
||||||
|
|
||||||
```Python hl_lines="2"
|
|
||||||
{!./src/using_request_directly/tutorial001.py!}
|
{!./src/using_request_directly/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Declare the `Request` parameter
|
By declaring a *path operation function* parameter with the type being the `Request` **FastAPI** will know to pass the `Request` in that parameter.
|
||||||
|
|
||||||
Then declare a *path operation function* parameter with the type being the `Request` class:
|
|
||||||
|
|
||||||
```Python hl_lines="8"
|
|
||||||
{!./src/using_request_directly/tutorial001.py!}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
Note that in this case, we are declaring a path parameter besides the request parameter.
|
Note that in this case, we are declaring a path parameter beside the request parameter.
|
||||||
|
|
||||||
So, the path parameter will be extracted, validated, converted to the specified type and annotated with OpenAPI.
|
So, the path parameter will be extracted, validated, converted to the specified type and annotated with OpenAPI.
|
||||||
|
|
||||||
|
|
@ -53,3 +43,8 @@ Then declare a *path operation function* parameter with the type being the `Requ
|
||||||
## `Request` documentation
|
## `Request` documentation
|
||||||
|
|
||||||
You can read more details about the <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request` object in the official Starlette documentation site</a>.
|
You can read more details about the <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request` object in the official Starlette documentation site</a>.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.requests import Request`.
|
||||||
|
|
||||||
|
**FastAPI** provides it directly just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ 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:
|
But it's the simplest way to focus on the server-side of WebSockets and have a working example:
|
||||||
|
|
||||||
```Python hl_lines="2 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 42 43 44"
|
```Python hl_lines="2 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 41 42 43"
|
||||||
{!./src/websockets/tutorial001.py!}
|
{!./src/websockets/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -31,18 +31,20 @@ But it's the simplest way to focus on the server-side of WebSockets and have a w
|
||||||
|
|
||||||
In your **FastAPI** application, create a `websocket`:
|
In your **FastAPI** application, create a `websocket`:
|
||||||
|
|
||||||
```Python hl_lines="3 47 48"
|
```Python hl_lines="1 46 47"
|
||||||
{!./src/websockets/tutorial001.py!}
|
{!./src/websockets/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! tip
|
!!! note "Technical Details"
|
||||||
In this example we are importing `WebSocket` from `starlette.websockets` to use it in the type declaration in the WebSocket route function.
|
You could also use `from starlette.websockets import WebSocket`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `WebSocket` directly just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
||||||
## Await for messages and send messages
|
## Await for messages and send messages
|
||||||
|
|
||||||
In your WebSocket route you can `await` for messages and send messages.
|
In your WebSocket route you can `await` for messages and send messages.
|
||||||
|
|
||||||
```Python hl_lines="49 50 51 52 53"
|
```Python hl_lines="48 49 50 51 52"
|
||||||
{!./src/websockets/tutorial001.py!}
|
{!./src/websockets/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -61,7 +63,7 @@ In WebSocket endpoints you can import from `fastapi` and use:
|
||||||
|
|
||||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||||
|
|
||||||
```Python hl_lines="55 56 57 58 59 60 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78"
|
```Python hl_lines="53 54 55 56 57 58 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76"
|
||||||
{!./src/websockets/tutorial002.py!}
|
{!./src/websockets/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -76,7 +78,6 @@ They work the same way as for other FastAPI endpoints/*path operations*:
|
||||||
|
|
||||||
To learn more about the options, check Starlette's documentation for:
|
To learn more about the options, check Starlette's documentation for:
|
||||||
|
|
||||||
* <a href="https://www.starlette.io/applications/" class="external-link" target="_blank">Applications (`websocket_route`)</a>.
|
|
||||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">The `WebSocket` class</a>.
|
||||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Class-based WebSocket handling</a>.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
You can mount WSGI applications as you saw with [Sub Applications - Behind a Proxy, Mounts](./sub-applications-proxy.md){.internal-link target=_blank}.
|
||||||
|
|
||||||
|
For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI application, for example, Flask, Django, etc.
|
||||||
|
|
||||||
|
## Using `WSGIMiddleware`
|
||||||
|
|
||||||
|
You need to import `WSGIMiddleware`.
|
||||||
|
|
||||||
|
Then wrap the WSGI (e.g. Flask) app with the middleware.
|
||||||
|
|
||||||
|
And then mount that under a path.
|
||||||
|
|
||||||
|
```Python hl_lines="1 3 22"
|
||||||
|
{!./src/wsgi/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check it
|
||||||
|
|
||||||
|
Now, every request under the path `/v1/` will be handled by the Flask application.
|
||||||
|
|
||||||
|
And the rest will be handled by **FastAPI**.
|
||||||
|
|
||||||
|
If you run it with Uvicorn and go to <a href="http://localhost:8000/v1/" class="external-link" target="_blank">http://localhost:8000/v1/</a> you will see the response from Flask:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Hello, World from Flask!
|
||||||
|
```
|
||||||
|
|
||||||
|
And if you go to <a href="http://localhost:8000/v2" class="external-link" target="_blank">http://localhost:8000/v2</a> you will see the response from FastAPI:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"message": "Hello World"
|
||||||
|
}
|
||||||
|
```
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 30 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
class Item(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.responses import FileResponse
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
class Item(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
class Item(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.responses import FileResponse
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
class Item(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from fastapi import Body, FastAPI
|
from fastapi import Body, FastAPI, status
|
||||||
from starlette.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from starlette.status import HTTP_201_CREATED
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -17,4 +16,4 @@ async def upsert_item(item_id: str, name: str = Body(None), size: int = Body(Non
|
||||||
else:
|
else:
|
||||||
item = {"name": name, "size": size}
|
item = {"name": name, "size": size}
|
||||||
items[item_id] = item
|
items[item_id] = item
|
||||||
return JSONResponse(status_code=HTTP_201_CREATED, content=item)
|
return JSONResponse(status_code=status.HTTP_201_CREATED, content=item)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
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"}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
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"}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.gzip import GZipMiddleware
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def main():
|
||||||
|
return "somebigcontent"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from .main import app
|
from .main import app
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from .main_b import app
|
from .main_b import app
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from starlette.websockets import WebSocket
|
from fastapi.websockets import WebSocket
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -17,3 +17,8 @@ app.add_middleware(
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def main():
|
||||||
|
return {"message": "Hello World"}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import gzip
|
import gzip
|
||||||
from typing import Callable, List
|
from typing import Callable, List
|
||||||
|
|
||||||
from fastapi import Body, FastAPI
|
from fastapi import Body, FastAPI, Request, Response
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
|
|
||||||
class GzipRequest(Request):
|
class GzipRequest(Request):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
from typing import Callable, List
|
from typing import Callable, List
|
||||||
|
|
||||||
from fastapi import Body, FastAPI, HTTPException
|
from fastapi import Body, FastAPI, HTTPException, Request, Response
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
|
|
||||||
class ValidationErrorLoggingRoute(APIRoute):
|
class ValidationErrorLoggingRoute(APIRoute):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import time
|
import time
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from fastapi import APIRouter, FastAPI
|
from fastapi import APIRouter, FastAPI, Request, Response
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
|
|
||||||
class TimedRoute(APIRoute):
|
class TimedRoute(APIRoute):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import UJSONResponse
|
from fastapi.responses import UJSONResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import PlainTextResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/", response_class=PlainTextResponse)
|
||||||
|
async def main():
|
||||||
|
return "Hello World"
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import RedirectResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/typer")
|
||||||
|
async def read_typer():
|
||||||
|
return RedirectResponse("https://typer.tiangolo.com")
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
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())
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
some_file_path = "large-video-file.mp4"
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def main():
|
||||||
|
file_like = open(some_file_path, mode="rb")
|
||||||
|
return StreamingResponse(file_like, media_type="video/mp4")
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
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,5 +1,5 @@
|
||||||
from fastapi import Depends, FastAPI
|
from fastapi import Depends, FastAPI
|
||||||
from starlette.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from fastapi.openapi.docs import (
|
||||||
get_swagger_ui_html,
|
get_swagger_ui_html,
|
||||||
get_swagger_ui_oauth2_redirect_html,
|
get_swagger_ui_oauth2_redirect_html,
|
||||||
)
|
)
|
||||||
from starlette.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
app = FastAPI(docs_url=None, redoc_url=None)
|
app = FastAPI(docs_url=None, redoc_url=None)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from starlette.requests import Request
|
from fastapi.responses import JSONResponse
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
|
|
||||||
class UnicornException(Exception):
|
class UnicornException(Exception):
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
|
from fastapi.responses import PlainTextResponse
|
||||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||||
from starlette.responses import PlainTextResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request, status
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette import status
|
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from starlette.requests import Request
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from fastapi import APIRouter, FastAPI
|
from fastapi import APIRouter, FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel, HttpUrl
|
from pydantic import BaseModel, HttpUrl
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.status import HTTP_201_CREATED
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -15,6 +14,6 @@ class Item(BaseModel):
|
||||||
tags: Set[str] = []
|
tags: Set[str] = []
|
||||||
|
|
||||||
|
|
||||||
@app.post("/items/", response_model=Item, status_code=HTTP_201_CREATED)
|
@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_item(*, item: Item):
|
async def create_item(*, item: Item):
|
||||||
return item
|
return item
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import FastAPI, File, UploadFile
|
from fastapi import FastAPI, File, UploadFile
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Response, status
|
||||||
from starlette.responses import Response
|
|
||||||
from starlette.status import HTTP_201_CREATED
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -11,5 +9,5 @@ tasks = {"foo": "Listen to the Bar Fighters"}
|
||||||
def get_or_create_task(task_id: str, response: Response):
|
def get_or_create_task(task_id: str, response: Response):
|
||||||
if task_id not in tasks:
|
if task_id not in tasks:
|
||||||
tasks[task_id] = "This didn't exist before"
|
tasks[task_id] = "This didn't exist before"
|
||||||
response.status_code = HTTP_201_CREATED
|
response.status_code = status.HTTP_201_CREATED
|
||||||
return tasks[task_id]
|
return tasks[task_id]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Response
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from datetime import datetime
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.responses import JSONResponse
|
|
||||||
|
|
||||||
|
|
||||||
class Item(BaseModel):
|
class Item(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Response
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Response
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, status
|
||||||
from starlette.status import HTTP_201_CREATED
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
@app.post("/items/", status_code=HTTP_201_CREATED)
|
@app.post("/items/", status_code=status.HTTP_201_CREATED)
|
||||||
async def create_item(name: str):
|
async def create_item(name: str):
|
||||||
return {"name": name}
|
return {"name": name}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
fake_users_db = {
|
fake_users_db = {
|
||||||
"johndoe": {
|
"johndoe": {
|
||||||
|
|
@ -58,7 +57,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||||
user = fake_decode_token(token)
|
user = fake_decode_token(token)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Invalid authentication credentials",
|
detail="Invalid authentication credentials",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||||
from jwt import PyJWTError
|
from jwt import PyJWTError
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
# to get a string like this run:
|
# to get a string like this run:
|
||||||
# openssl rand -hex 32
|
# openssl rand -hex 32
|
||||||
|
|
@ -89,7 +88,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||||
|
|
||||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Could not validate credentials",
|
detail="Could not validate credentials",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
@ -118,7 +117,7 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
|
||||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Incorrect username or password",
|
detail="Incorrect username or password",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
from fastapi import Depends, FastAPI, HTTPException, Security, status
|
||||||
from fastapi.security import (
|
from fastapi.security import (
|
||||||
OAuth2PasswordBearer,
|
OAuth2PasswordBearer,
|
||||||
OAuth2PasswordRequestForm,
|
OAuth2PasswordRequestForm,
|
||||||
|
|
@ -11,7 +11,6 @@ from fastapi.security import (
|
||||||
from jwt import PyJWTError
|
from jwt import PyJWTError
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from pydantic import BaseModel, ValidationError
|
from pydantic import BaseModel, ValidationError
|
||||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
# to get a string like this run:
|
# to get a string like this run:
|
||||||
# openssl rand -hex 32
|
# openssl rand -hex 32
|
||||||
|
|
@ -111,7 +110,7 @@ async def get_current_user(
|
||||||
else:
|
else:
|
||||||
authenticate_value = f"Bearer"
|
authenticate_value = f"Bearer"
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Could not validate credentials",
|
detail="Could not validate credentials",
|
||||||
headers={"WWW-Authenticate": authenticate_value},
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
)
|
)
|
||||||
|
|
@ -130,7 +129,7 @@ async def get_current_user(
|
||||||
for scope in security_scopes.scopes:
|
for scope in security_scopes.scopes:
|
||||||
if scope not in token_data.scopes:
|
if scope not in token_data.scopes:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Not enough permissions",
|
detail="Not enough permissions",
|
||||||
headers={"WWW-Authenticate": authenticate_value},
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import secrets
|
import secrets
|
||||||
|
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException, status
|
||||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -14,7 +13,7 @@ def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
|
||||||
correct_password = secrets.compare_digest(credentials.password, "swordfish")
|
correct_password = secrets.compare_digest(credentials.password, "swordfish")
|
||||||
if not (correct_username and correct_password):
|
if not (correct_username and correct_password):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Incorrect email or password",
|
detail="Incorrect email or password",
|
||||||
headers={"WWW-Authenticate": "Basic"},
|
headers={"WWW-Authenticate": "Basic"},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException, Request, Response
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from starlette.requests import Request
|
|
||||||
from starlette.responses import Response
|
|
||||||
|
|
||||||
from . import crud, models, schemas
|
from . import crud, models, schemas
|
||||||
from .database import SessionLocal, engine
|
from .database import SessionLocal, engine
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from starlette.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from starlette.requests import Request
|
from fastapi.staticfiles import StaticFiles
|
||||||
from starlette.staticfiles import StaticFiles
|
from fastapi.templating import Jinja2Templates
|
||||||
from starlette.templating import Jinja2Templates
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from starlette.requests import Request
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, WebSocket
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from starlette.websockets import WebSocket
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
from fastapi import Cookie, Depends, FastAPI, Header
|
from fastapi import Cookie, Depends, FastAPI, Header, WebSocket, status
|
||||||
from starlette.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from starlette.status import WS_1008_POLICY_VIOLATION
|
|
||||||
from starlette.websockets import WebSocket
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
@ -56,7 +54,7 @@ async def get_cookie_or_client(
|
||||||
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
|
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
|
||||||
):
|
):
|
||||||
if session is None and x_client is None:
|
if session is None and x_client is None:
|
||||||
await websocket.close(code=WS_1008_POLICY_VIOLATION)
|
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
||||||
return session or x_client
|
return session or x_client
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from flask import Flask, escape, request
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||||
|
|
||||||
|
flask_app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@flask_app.route("/")
|
||||||
|
def flask_main():
|
||||||
|
name = request.args.get("name", "World")
|
||||||
|
return f"Hello, {escape(name)} from Flask!"
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/v2")
|
||||||
|
def read_main():
|
||||||
|
return {"message": "Hello World"}
|
||||||
|
|
||||||
|
|
||||||
|
app.mount("/v1", WSGIMiddleware(flask_app))
|
||||||
|
|
@ -32,9 +32,9 @@ So, for everything to work correctly, it's better to specify explicitly the allo
|
||||||
|
|
||||||
## Use `CORSMiddleware`
|
## Use `CORSMiddleware`
|
||||||
|
|
||||||
You can configure it in your **FastAPI** application using Starlette's <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">`CORSMiddleware`</a>.
|
You can configure it in your **FastAPI** application using the `CORSMiddleware`.
|
||||||
|
|
||||||
* Import it from Starlette.
|
* Import `CORSMiddleware`.
|
||||||
* Create a list of allowed origins (as strings).
|
* Create a list of allowed origins (as strings).
|
||||||
* Add it as a "middleware" to your **FastAPI** application.
|
* Add it as a "middleware" to your **FastAPI** application.
|
||||||
|
|
||||||
|
|
@ -44,12 +44,39 @@ You can also specify if your backend allows:
|
||||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||||
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
||||||
|
|
||||||
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
||||||
{!./src/cors/tutorial001.py!}
|
{!./src/cors/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `allow_origins` - A list of origins that should be permitted to make cross-origin requests. E.g. `['https://example.org', 'https://www.example.org']`. You can use `['*']` to allow any origin.
|
||||||
|
* `allow_origin_regex` - A regex string to match against origins that should be permitted to make cross-origin requests. eg. `'https://.*\.example\.org'`.
|
||||||
|
* `allow_methods` - A list of HTTP methods that should be allowed for cross-origin requests. Defaults to `['GET']`. You can use `['*']` to allow all standard methods.
|
||||||
|
* `allow_headers` - A list of HTTP request headers that should be supported for cross-origin requests. Defaults to `[]`. You can use `['*']` to allow all headers. The `Accept`, `Accept-Language`, `Content-Language` and `Content-Type` headers are always allowed for CORS requests.
|
||||||
|
* `allow_credentials` - Indicate that cookies should be supported for cross-origin requests. Defaults to `False`.
|
||||||
|
* `expose_headers` - Indicate any response headers that should be made accessible to the browser. Defaults to `[]`.
|
||||||
|
* `max_age` - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults to `60`.
|
||||||
|
|
||||||
|
The middleware responds to two particular types of HTTP request...
|
||||||
|
|
||||||
|
### CORS preflight requests
|
||||||
|
|
||||||
|
These are any `OPTIONS` request with `Origin` and `Access-Control-Request-Method` headers.
|
||||||
|
|
||||||
|
In this case the middleware will intercept the incoming request and respond with appropriate CORS headers, and either a `200` or `400` response for informational purposes.
|
||||||
|
|
||||||
|
### Simple requests
|
||||||
|
|
||||||
|
Any request with an `Origin` header. In this case the middleware will pass the request through as normal, but will include appropriate CORS headers on the response.
|
||||||
|
|
||||||
## More info
|
## More info
|
||||||
|
|
||||||
For more details of what you can specify in `CORSMiddleware`, check <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's `CORSMiddleware` docs</a>.
|
|
||||||
|
|
||||||
For more info about <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS documentation</a>.
|
For more info about <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" class="external-link" target="_blank">Mozilla CORS documentation</a>.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.middleware.cors import CORSMiddleware`.
|
||||||
|
|
||||||
|
**FastAPI** provides several middlewares in `fastapi.middleware` just as a convenience for you, the developer. But most of the available middlewares come directly from Starlette.
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ And you want to handle this exception globally with FastAPI.
|
||||||
|
|
||||||
You could add a custom exception handler with `@app.exception_handler()`:
|
You could add a custom exception handler with `@app.exception_handler()`:
|
||||||
|
|
||||||
```Python hl_lines="6 7 8 14 15 16 17 18 24"
|
```Python hl_lines="5 6 7 13 14 15 16 17 18 24"
|
||||||
{!./src/handling_errors/tutorial003.py!}
|
{!./src/handling_errors/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -104,6 +104,11 @@ So, you will receive a clean error, with an HTTP status code of `418` and a JSON
|
||||||
{"message": "Oops! yolo did something. There goes a rainbow..."}
|
{"message": "Oops! yolo did something. There goes a rainbow..."}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.requests import Request` and `from starlette.responses import JSONResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette. The same with `Request`.
|
||||||
|
|
||||||
## Override the default exception handlers
|
## Override the default exception handlers
|
||||||
|
|
||||||
**FastAPI** has some default exception handlers.
|
**FastAPI** has some default exception handlers.
|
||||||
|
|
@ -172,17 +177,22 @@ 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:
|
For example, you could want to return a plain text response instead of JSON for these errors:
|
||||||
|
|
||||||
```Python hl_lines="1 3 9 10 11 22"
|
```Python hl_lines="3 4 9 10 11 22"
|
||||||
{!./src/handling_errors/tutorial004.py!}
|
{!./src/handling_errors/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import PlainTextResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
### Use the `RequestValidationError` body
|
### Use the `RequestValidationError` body
|
||||||
|
|
||||||
The `RequestValidationError` contains the `body` it received with invalid data.
|
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.
|
You could use it while developing your app to log the body and debug it, return it to the user, etc.
|
||||||
|
|
||||||
```Python hl_lines="16"
|
```Python hl_lines="14"
|
||||||
{!./src/handling_errors/tutorial005.py!}
|
{!./src/handling_errors/tutorial005.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -231,7 +241,7 @@ So, you can keep raising **FastAPI**'s `HTTPException` as normally in your code.
|
||||||
|
|
||||||
But when you register an exception handler, you should register it for Starlette's `HTTPException`.
|
But when you register an exception handler, you should register it for Starlette's `HTTPException`.
|
||||||
|
|
||||||
This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises an `HTTPException`, your handler will be able to catch and handle it.
|
This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette `HTTPException`, your handler will be able to catch and handle it.
|
||||||
|
|
||||||
In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`:
|
In this example, to be able to have both `HTTPException`s in the same code, Starlette's exceptions is renamed to `StarletteHTTPException`:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@ A "middleware" is a function that works with every **request** before it is proc
|
||||||
* It can do something to that **response** or run any needed code.
|
* It can do something to that **response** or run any needed code.
|
||||||
* Then it returns the **response**.
|
* Then it returns the **response**.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
If you have dependencies with `yield`, the exit code will run *after* the middleware.
|
||||||
|
|
||||||
|
If there were any background tasks (documented later), they will run *after* all the middleware.
|
||||||
|
|
||||||
## Create a middleware
|
## Create a middleware
|
||||||
|
|
||||||
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
|
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
|
||||||
|
|
@ -21,7 +26,7 @@ The middleware function receives:
|
||||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||||
* You can then modify further the `response` before returning it.
|
* You can then modify further the `response` before returning it.
|
||||||
|
|
||||||
```Python hl_lines="9 10 12 15"
|
```Python hl_lines="8 9 11 14"
|
||||||
{!./src/middleware/tutorial001.py!}
|
{!./src/middleware/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -30,6 +35,11 @@ The middleware function receives:
|
||||||
|
|
||||||
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
|
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md){.internal-link target=_blank}) using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" class="external-link" target="_blank">Starlette's CORS docs</a>.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.requests import Request`.
|
||||||
|
|
||||||
|
**FastAPI** provides it as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
||||||
### Before and after the `response`
|
### Before and after the `response`
|
||||||
|
|
||||||
You can add code to be run with the `request`, before any *path operation* receives it.
|
You can add code to be run with the `request`, before any *path operation* receives it.
|
||||||
|
|
@ -38,19 +48,12 @@ 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:
|
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:
|
||||||
|
|
||||||
```Python hl_lines="11 13 14"
|
```Python hl_lines="10 12 13"
|
||||||
{!./src/middleware/tutorial001.py!}
|
{!./src/middleware/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Starlette's Middleware
|
## Other middlewares
|
||||||
|
|
||||||
You can also add any other <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">Starlette Middleware</a>.
|
You can later read more about other middlewares in the [Advanced User Guide: Advanced Middleware](../advanced/middleware.md){.internal-link target=_blank}.
|
||||||
|
|
||||||
These are classes instead of plain functions.
|
You will read about how to handle <abbr title="Cross-Origin Resource Sharing">CORS</abbr> with a middleware in the next section.
|
||||||
|
|
||||||
Including:
|
|
||||||
|
|
||||||
* `CORSMiddleware` (described in the next section).
|
|
||||||
* `GZipMiddleware`.
|
|
||||||
* `SentryMiddleware`.
|
|
||||||
* ...and others.
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,19 @@ You can define the (HTTP) `status_code` to be used in the response of your *path
|
||||||
|
|
||||||
You can pass directly the `int` code, like `404`.
|
You can pass directly the `int` code, like `404`.
|
||||||
|
|
||||||
But if you don't remember what each number code is for, you can use the shortcut constants from `starlette`:
|
But if you don't remember what each number code is for, you can use the shortcut constants in `status`:
|
||||||
|
|
||||||
```Python hl_lines="5 18"
|
```Python hl_lines="3 17"
|
||||||
{!./src/path_operation_configuration/tutorial001.py!}
|
{!./src/path_operation_configuration/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
That status code will be used in the response and will be added to the OpenAPI schema.
|
That status code will be used in the response and will be added to the OpenAPI schema.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette import status`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,11 @@ You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||||
|
|
||||||
So, whenever Swagger UI supports multi-file uploads, or any other tools that supports OpenAPI, they will be compatible with **FastAPI**.
|
So, whenever Swagger UI supports multi-file uploads, or any other tools that supports OpenAPI, they will be compatible with **FastAPI**.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.responses import HTMLResponse`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
Use `File` to declare files to be uploaded as input parameters (as form data).
|
Use `File` to declare files to be uploaded as input parameters (as form data).
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ Let's see the previous example again:
|
||||||
|
|
||||||
But you don't have to memorize what each of these codes mean.
|
But you don't have to memorize what each of these codes mean.
|
||||||
|
|
||||||
You can use the convenience variables from `starlette.status`.
|
You can use the convenience variables from `fastapi.status`.
|
||||||
|
|
||||||
```Python hl_lines="2 7"
|
```Python hl_lines="1 6"
|
||||||
{!./src/response_status_code/tutorial002.py!}
|
{!./src/response_status_code/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -74,6 +74,11 @@ They are just a convenience, they hold the same number, but that way you can use
|
||||||
|
|
||||||
<img src="/img/tutorial/response-status-code/image02.png">
|
<img src="/img/tutorial/response-status-code/image02.png">
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette import status`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.status` as `fastapi.status` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
||||||
## Changing the default
|
## Changing the default
|
||||||
|
|
||||||
Later, in the **Advanced User Guide**, you will see how to return a different status code than the default you are declaring here.
|
Later, in the **Advanced User Guide**, you will see how to return a different status code than the default you are declaring here.
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ And another utility to verify if a received password matches the hash stored.
|
||||||
|
|
||||||
And another one to authenticate and return a user.
|
And another one to authenticate and return a user.
|
||||||
|
|
||||||
```Python hl_lines="7 39 56 57 60 61 70 71 72 73 74 75 76"
|
```Python hl_lines="7 48 55 56 59 60 69 70 71 72 73 74 75"
|
||||||
{!./src/security/tutorial004.py!}
|
{!./src/security/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ Define a Pydantic Model that will be used in the token endpoint for the response
|
||||||
|
|
||||||
Create a utility function to generate a new access token.
|
Create a utility function to generate a new access token.
|
||||||
|
|
||||||
```Python hl_lines="3 6 13 14 15 29 30 31 79 80 81 82 83 84 85 86 87"
|
```Python hl_lines="3 6 12 13 14 28 29 30 78 79 80 81 82 83 84 85 86"
|
||||||
{!./src/security/tutorial004.py!}
|
{!./src/security/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ Decode the received token, verify it, and return the current user.
|
||||||
|
|
||||||
If the token is invalid, return an HTTP error right away.
|
If the token is invalid, return an HTTP error right away.
|
||||||
|
|
||||||
```Python hl_lines="90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107"
|
```Python hl_lines="89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106"
|
||||||
{!./src/security/tutorial004.py!}
|
{!./src/security/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ Create a `timedelta` with the expiration time of the token.
|
||||||
|
|
||||||
Create a real JWT access token and return it.
|
Create a real JWT access token and return it.
|
||||||
|
|
||||||
```Python hl_lines="116 117 118 119 120 121 122 123 124 125 126 127 128 129"
|
```Python hl_lines="115 116 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||||
{!./src/security/tutorial004.py!}
|
{!./src/security/tutorial004.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ Now let's use the utilities provided by **FastAPI** to handle this.
|
||||||
|
|
||||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` for the path `/token`:
|
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` for the path `/token`:
|
||||||
|
|
||||||
```Python hl_lines="2 75"
|
```Python hl_lines="2 74"
|
||||||
{!./src/security/tutorial003.py!}
|
{!./src/security/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ If there is no such user, we return an error saying "incorrect username or passw
|
||||||
|
|
||||||
For the error, we use the exception `HTTPException`:
|
For the error, we use the exception `HTTPException`:
|
||||||
|
|
||||||
```Python hl_lines="1 76 77 78"
|
```Python hl_lines="1 75 76 77"
|
||||||
{!./src/security/tutorial003.py!}
|
{!./src/security/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ If your database is stolen, the thief won't have your users' plaintext passwords
|
||||||
|
|
||||||
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
|
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
|
||||||
|
|
||||||
```Python hl_lines="79 80 81 82"
|
```Python hl_lines="78 79 80 81"
|
||||||
{!./src/security/tutorial003.py!}
|
{!./src/security/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -154,7 +154,7 @@ For this simple example, we are going to just be completely insecure and return
|
||||||
|
|
||||||
But for now, let's focus on the specific details we need.
|
But for now, let's focus on the specific details we need.
|
||||||
|
|
||||||
```Python hl_lines="84"
|
```Python hl_lines="83"
|
||||||
{!./src/security/tutorial003.py!}
|
{!./src/security/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -179,7 +179,7 @@ Both of these dependencies will just return an HTTP error if the user doesn't ex
|
||||||
|
|
||||||
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
|
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
|
||||||
|
|
||||||
```Python hl_lines="57 58 59 60 61 62 63 64 65 68 69 70 71 88"
|
```Python hl_lines="56 57 58 59 60 61 62 63 64 65 67 68 69 70 88"
|
||||||
{!./src/security/tutorial003.py!}
|
{!./src/security/tutorial003.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -626,7 +626,7 @@ A "middleware" is basically a function that is always executed for each request,
|
||||||
|
|
||||||
The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished.
|
The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished.
|
||||||
|
|
||||||
```Python hl_lines="16 17 18 19 20 21 22 23 24"
|
```Python hl_lines="14 15 16 17 18 19 20 21 22"
|
||||||
{!./src/sql_databases/sql_app/alt_main.py!}
|
{!./src/sql_databases/sql_app/alt_main.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -639,7 +639,7 @@ The middleware we'll add (just a function) will create a new SQLAlchemy `Session
|
||||||
|
|
||||||
### About `request.state`
|
### About `request.state`
|
||||||
|
|
||||||
<a href="https://www.starlette.io/requests/#other-state" class="external-link" target="_blank">`request.state` is a property of each Starlette `Request` object</a>. It is there to store arbitrary objects attached to the request itself, like the database session in this case.
|
`request.state` is a property of each `Request` object. It is there to store arbitrary objects attached to the request itself, like the database session in this case. You can read more about it in <a href="https://www.starlette.io/requests/#other-state" class="external-link" target="_blank">Starlette's docs about `Request` state</a>.
|
||||||
|
|
||||||
For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware).
|
For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
You can serve static files automatically from a directory using <a href="https://www.starlette.io/staticfiles/" class="external-link" target="_blank">Starlette's Static Files</a>.
|
You can serve static files automatically from a directory using `StaticFiles`.
|
||||||
|
|
||||||
## Install `aiofiles`
|
## Install `aiofiles`
|
||||||
|
|
||||||
|
|
@ -10,13 +10,18 @@ pip install aiofiles
|
||||||
|
|
||||||
## Use `StaticFiles`
|
## Use `StaticFiles`
|
||||||
|
|
||||||
* Import `StaticFiles` from Starlette.
|
* Import `StaticFiles`.
|
||||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||||
|
|
||||||
```Python hl_lines="2 6"
|
```Python hl_lines="2 6"
|
||||||
{!./src/static_files/tutorial001.py!}
|
{!./src/static_files/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.staticfiles import StaticFiles`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.staticfiles` as `fastapi.staticfiles` just as a convenience for you, the developer. But it actually comes directly from Starlette.
|
||||||
|
|
||||||
### What is "Mounting"
|
### What is "Mounting"
|
||||||
|
|
||||||
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
|
"Mounting" means adding a complete "independent" application in a specific path, that then takes care of handling all the sub-paths.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette's TestClient</a>, testing **FastAPI** applications is easy and enjoyable.
|
Thanks to <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, testing **FastAPI** applications is easy and enjoyable.
|
||||||
|
|
||||||
It is based on <a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a>, so it's very familiar and intuitive.
|
It is based on <a href="http://docs.python-requests.org" class="external-link" target="_blank">Requests</a>, so it's very familiar and intuitive.
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ With it, you can use <a href="https://docs.pytest.org/" class="external-link" ta
|
||||||
|
|
||||||
## Using `TestClient`
|
## Using `TestClient`
|
||||||
|
|
||||||
Import `TestClient` from `starlette.testclient`.
|
Import `TestClient`.
|
||||||
|
|
||||||
Create a `TestClient` passing to it your **FastAPI**.
|
Create a `TestClient` passing to it your **FastAPI**.
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ Use the `TestClient` object the same way as you do with `requests`.
|
||||||
|
|
||||||
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
|
Write simple `assert` statements with the standard Python expressions that you need to check (again, standard `pytest`).
|
||||||
|
|
||||||
```Python hl_lines="2 12 15 16 17 18"
|
```Python hl_lines="2 12 15 16 17 18"
|
||||||
{!./src/app_testing/tutorial001.py!}
|
{!./src/app_testing/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -27,6 +27,11 @@ Write simple `assert` statements with the standard Python expressions that you n
|
||||||
|
|
||||||
This allows you to use `pytest` directly without complications.
|
This allows you to use `pytest` directly without complications.
|
||||||
|
|
||||||
|
!!! note "Technical Details"
|
||||||
|
You could also use `from starlette.testclient import TestClient`.
|
||||||
|
|
||||||
|
**FastAPI** provides the same `starlette.testclient` as `fastapi.testclient` just as a convenience for you, the developer. But it comes directly from Starlette.
|
||||||
|
|
||||||
## Separating tests
|
## Separating tests
|
||||||
|
|
||||||
In a real application, you probably would have your tests in a different file.
|
In a real application, you probably would have your tests in a different file.
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
__version__ = "0.50.0"
|
__version__ = "0.50.0"
|
||||||
|
|
||||||
from starlette.background import BackgroundTasks
|
from starlette import status
|
||||||
|
|
||||||
from .applications import FastAPI
|
from .applications import FastAPI
|
||||||
|
from .background import BackgroundTasks
|
||||||
from .datastructures import UploadFile
|
from .datastructures import UploadFile
|
||||||
from .exceptions import HTTPException
|
from .exceptions import HTTPException
|
||||||
from .param_functions import (
|
from .param_functions import (
|
||||||
|
|
@ -18,4 +19,7 @@ from .param_functions import (
|
||||||
Query,
|
Query,
|
||||||
Security,
|
Security,
|
||||||
)
|
)
|
||||||
|
from .requests import Request
|
||||||
|
from .responses import Response
|
||||||
from .routing import APIRouter
|
from .routing import APIRouter
|
||||||
|
from .websockets import WebSocket
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.background import BackgroundTasks # noqa
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from starlette.concurrency import iterate_in_threadpool # noqa
|
from starlette.concurrency import iterate_in_threadpool # noqa
|
||||||
from starlette.concurrency import run_in_threadpool
|
from starlette.concurrency import run_in_threadpool # noqa
|
||||||
|
from starlette.concurrency import run_until_first_complete # noqa
|
||||||
|
|
||||||
asynccontextmanager_error_message = """
|
asynccontextmanager_error_message = """
|
||||||
FastAPI's contextmanager_in_threadpool require Python 3.7 or above,
|
FastAPI's contextmanager_in_threadpool require Python 3.7 or above,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware import Middleware
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware.cors import CORSMiddleware # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware.gzip import GZipMiddleware # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware.trustedhost import TrustedHostMiddleware # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.middleware.wsgi import WSGIMiddleware # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.requests import Request # noqa
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
from starlette.responses import FileResponse # noqa
|
||||||
|
from starlette.responses import HTMLResponse # noqa
|
||||||
|
from starlette.responses import JSONResponse # noqa
|
||||||
|
from starlette.responses import PlainTextResponse # noqa
|
||||||
|
from starlette.responses import RedirectResponse # noqa
|
||||||
|
from starlette.responses import Response # noqa
|
||||||
|
from starlette.responses import StreamingResponse # noqa
|
||||||
|
from starlette.responses import UJSONResponse # noqa
|
||||||
|
|
@ -29,6 +29,7 @@ from starlette.concurrency import run_in_threadpool
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.responses import JSONResponse, Response
|
from starlette.responses import JSONResponse, Response
|
||||||
|
from starlette.routing import Mount # noqa
|
||||||
from starlette.routing import (
|
from starlette.routing import (
|
||||||
compile_path,
|
compile_path,
|
||||||
get_name,
|
get_name,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.staticfiles import StaticFiles # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.templating import Jinja2Templates # noqa
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from starlette.testclient import TestClient # noqa
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
from starlette.websockets import WebSocket # noqa
|
||||||
|
from starlette.websockets import WebSocketDisconnect # noqa
|
||||||
16
mkdocs.yml
16
mkdocs.yml
|
|
@ -51,14 +51,14 @@ nav:
|
||||||
- Sub-dependencies: 'tutorial/dependencies/sub-dependencies.md'
|
- Sub-dependencies: 'tutorial/dependencies/sub-dependencies.md'
|
||||||
- Dependencies in path operation decorators: 'tutorial/dependencies/dependencies-in-path-operation-decorators.md'
|
- Dependencies in path operation decorators: 'tutorial/dependencies/dependencies-in-path-operation-decorators.md'
|
||||||
- Dependencies with yield: 'tutorial/dependencies/dependencies-with-yield.md'
|
- Dependencies with yield: 'tutorial/dependencies/dependencies-with-yield.md'
|
||||||
- Security:
|
- Security:
|
||||||
- Security Intro: 'tutorial/security/index.md'
|
- Security Intro: 'tutorial/security/index.md'
|
||||||
- First Steps: 'tutorial/security/first-steps.md'
|
- First Steps: 'tutorial/security/first-steps.md'
|
||||||
- Get Current User: 'tutorial/security/get-current-user.md'
|
- Get Current User: 'tutorial/security/get-current-user.md'
|
||||||
- Simple OAuth2 with Password and Bearer: 'tutorial/security/simple-oauth2.md'
|
- Simple OAuth2 with Password and Bearer: 'tutorial/security/simple-oauth2.md'
|
||||||
- OAuth2 with Password (and hashing), Bearer with JWT tokens: 'tutorial/security/oauth2-jwt.md'
|
- OAuth2 with Password (and hashing), Bearer with JWT tokens: 'tutorial/security/oauth2-jwt.md'
|
||||||
- Middleware: 'tutorial/middleware.md'
|
- Middleware: 'tutorial/middleware.md'
|
||||||
- CORS (Cross-Origin Resource Sharing): 'tutorial/cors.md'
|
- CORS (Cross-Origin Resource Sharing): 'tutorial/cors.md'
|
||||||
- SQL (Relational) Databases: 'tutorial/sql-databases.md'
|
- SQL (Relational) Databases: 'tutorial/sql-databases.md'
|
||||||
- Bigger Applications - Multiple Files: 'tutorial/bigger-applications.md'
|
- Bigger Applications - Multiple Files: 'tutorial/bigger-applications.md'
|
||||||
- Background Tasks: 'tutorial/background-tasks.md'
|
- Background Tasks: 'tutorial/background-tasks.md'
|
||||||
|
|
@ -71,21 +71,22 @@ nav:
|
||||||
- Path Operation Advanced Configuration: 'advanced/path-operation-advanced-configuration.md'
|
- Path Operation Advanced Configuration: 'advanced/path-operation-advanced-configuration.md'
|
||||||
- Additional Status Codes: 'advanced/additional-status-codes.md'
|
- Additional Status Codes: 'advanced/additional-status-codes.md'
|
||||||
- Return a Response Directly: 'advanced/response-directly.md'
|
- Return a Response Directly: 'advanced/response-directly.md'
|
||||||
- Custom Response Class: 'advanced/custom-response.md'
|
- Custom Response - HTML, Stream, File, others: 'advanced/custom-response.md'
|
||||||
- Additional Responses in OpenAPI: 'advanced/additional-responses.md'
|
- Additional Responses in OpenAPI: 'advanced/additional-responses.md'
|
||||||
- Response Cookies: 'advanced/response-cookies.md'
|
- Response Cookies: 'advanced/response-cookies.md'
|
||||||
- Response Headers: 'advanced/response-headers.md'
|
- Response Headers: 'advanced/response-headers.md'
|
||||||
- Response - Change Status Code: 'advanced/response-change-status-code.md'
|
- Response - Change Status Code: 'advanced/response-change-status-code.md'
|
||||||
- Advanced Dependencies: 'advanced/advanced-dependencies.md'
|
- Advanced Dependencies: 'advanced/advanced-dependencies.md'
|
||||||
- Advanced Security:
|
- Advanced Security:
|
||||||
- Advanced Security - Intro: advanced/security/index.md
|
- Advanced Security - Intro: 'advanced/security/index.md'
|
||||||
- OAuth2 scopes: 'advanced/security/oauth2-scopes.md'
|
- OAuth2 scopes: 'advanced/security/oauth2-scopes.md'
|
||||||
- HTTP Basic Auth: 'advanced/security/http-basic-auth.md'
|
- HTTP Basic Auth: 'advanced/security/http-basic-auth.md'
|
||||||
- Using the Request Directly: 'advanced/using-request-directly.md'
|
- Using the Request Directly: 'advanced/using-request-directly.md'
|
||||||
|
- Advanced Middleware: 'advanced/middleware.md'
|
||||||
- SQL (Relational) Databases with Peewee: 'advanced/sql-databases-peewee.md'
|
- SQL (Relational) Databases with Peewee: 'advanced/sql-databases-peewee.md'
|
||||||
- Async SQL (Relational) Databases: 'advanced/async-sql-databases.md'
|
- Async SQL (Relational) Databases: 'advanced/async-sql-databases.md'
|
||||||
- NoSQL (Distributed / Big Data) Databases: 'advanced/nosql-databases.md'
|
- NoSQL (Distributed / Big Data) Databases: 'advanced/nosql-databases.md'
|
||||||
- Sub Applications - Behind a Proxy: 'advanced/sub-applications-proxy.md'
|
- Sub Applications - Behind a Proxy, Mounts: 'advanced/sub-applications-proxy.md'
|
||||||
- Templates: 'advanced/templates.md'
|
- Templates: 'advanced/templates.md'
|
||||||
- GraphQL: 'advanced/graphql.md'
|
- GraphQL: 'advanced/graphql.md'
|
||||||
- WebSockets: 'advanced/websockets.md'
|
- WebSockets: 'advanced/websockets.md'
|
||||||
|
|
@ -96,6 +97,7 @@ nav:
|
||||||
- Testing Dependencies with Overrides: 'advanced/testing-dependencies.md'
|
- Testing Dependencies with Overrides: 'advanced/testing-dependencies.md'
|
||||||
- Extending OpenAPI: 'advanced/extending-openapi.md'
|
- Extending OpenAPI: 'advanced/extending-openapi.md'
|
||||||
- OpenAPI Callbacks: 'advanced/openapi-callbacks.md'
|
- OpenAPI Callbacks: 'advanced/openapi-callbacks.md'
|
||||||
|
- Including WSGI - Flask, Django, others: 'advanced/wsgi.md'
|
||||||
- Concurrency and async / await: 'async.md'
|
- Concurrency and async / await: 'async.md'
|
||||||
- Deployment: 'deployment.md'
|
- Deployment: 'deployment.md'
|
||||||
- Project Generation - Template: 'project-generation.md'
|
- Project Generation - Template: 'project-generation.md'
|
||||||
|
|
@ -105,7 +107,7 @@ nav:
|
||||||
- Benchmarks: 'benchmarks.md'
|
- Benchmarks: 'benchmarks.md'
|
||||||
- Help FastAPI - Get Help: 'help-fastapi.md'
|
- Help FastAPI - Get Help: 'help-fastapi.md'
|
||||||
- Development - Contributing: 'contributing.md'
|
- Development - Contributing: 'contributing.md'
|
||||||
- Release Notes: release-notes.md
|
- Release Notes: 'release-notes.md'
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- toc:
|
- toc:
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ test = [
|
||||||
"async_generator",
|
"async_generator",
|
||||||
"python-multipart",
|
"python-multipart",
|
||||||
"aiofiles",
|
"aiofiles",
|
||||||
"ujson"
|
"ujson",
|
||||||
|
"flask"
|
||||||
]
|
]
|
||||||
doc = [
|
doc = [
|
||||||
"mkdocs",
|
"mkdocs",
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue