mirror of
https://github.com/fastapi/fastapi.git
synced 2026-07-28 23:57:37 -04:00
✨ 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:
committed by
GitHub
parent
f2bd2c44e2
commit
0ac9b3ee5c
@@ -32,9 +32,9 @@ So, for everything to work correctly, it's better to specify explicitly the allo
|
||||
|
||||
## 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).
|
||||
* 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 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!}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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>.
|
||||
|
||||
!!! 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()`:
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
@@ -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..."}
|
||||
```
|
||||
|
||||
!!! 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
|
||||
|
||||
**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:
|
||||
|
||||
```Python hl_lines="1 3 9 10 11 22"
|
||||
```Python hl_lines="3 4 9 10 11 22"
|
||||
{!./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
|
||||
|
||||
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.
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="14"
|
||||
{!./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`.
|
||||
|
||||
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`:
|
||||
|
||||
|
||||
@@ -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.
|
||||
* 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
|
||||
|
||||
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*.
|
||||
* 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!}
|
||||
```
|
||||
|
||||
@@ -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>.
|
||||
|
||||
!!! 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`
|
||||
|
||||
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:
|
||||
|
||||
```Python hl_lines="11 13 14"
|
||||
```Python hl_lines="10 12 13"
|
||||
{!./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.
|
||||
|
||||
Including:
|
||||
|
||||
* `CORSMiddleware` (described in the next section).
|
||||
* `GZipMiddleware`.
|
||||
* `SentryMiddleware`.
|
||||
* ...and others.
|
||||
You will read about how to handle <abbr title="Cross-Origin Resource Sharing">CORS</abbr> with a middleware in the next section.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
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!}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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**.
|
||||
|
||||
!!! 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
|
||||
|
||||
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.
|
||||
|
||||
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!}
|
||||
```
|
||||
|
||||
@@ -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">
|
||||
|
||||
!!! 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
|
||||
|
||||
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.
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
@@ -137,7 +137,7 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
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!}
|
||||
```
|
||||
|
||||
|
||||
@@ -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`:
|
||||
|
||||
```Python hl_lines="2 75"
|
||||
```Python hl_lines="2 74"
|
||||
{!./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`:
|
||||
|
||||
```Python hl_lines="1 76 77 78"
|
||||
```Python hl_lines="1 75 76 77"
|
||||
{!./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).
|
||||
|
||||
```Python hl_lines="79 80 81 82"
|
||||
```Python hl_lines="78 79 80 81"
|
||||
{!./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.
|
||||
|
||||
```Python hl_lines="84"
|
||||
```Python hl_lines="83"
|
||||
{!./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:
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
```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!}
|
||||
```
|
||||
|
||||
@@ -639,7 +639,7 @@ The middleware we'll add (just a function) will create a new SQLAlchemy `Session
|
||||
|
||||
### 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).
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -10,13 +10,18 @@ pip install aiofiles
|
||||
|
||||
## Use `StaticFiles`
|
||||
|
||||
* Import `StaticFiles` from Starlette.
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
```Python hl_lines="2 6"
|
||||
{!./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"
|
||||
|
||||
"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.
|
||||
|
||||
@@ -6,7 +6,7 @@ With it, you can use <a href="https://docs.pytest.org/" class="external-link" ta
|
||||
|
||||
## Using `TestClient`
|
||||
|
||||
Import `TestClient` from `starlette.testclient`.
|
||||
Import `TestClient`.
|
||||
|
||||
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`).
|
||||
|
||||
```Python hl_lines="2 12 15 16 17 18"
|
||||
```Python hl_lines="2 12 15 16 17 18"
|
||||
{!./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.
|
||||
|
||||
!!! 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
|
||||
|
||||
In a real application, you probably would have your tests in a different file.
|
||||
|
||||
Reference in New Issue
Block a user