mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into fix-duplicate-special-dependency-handling
This commit is contained in:
commit
c483dde8d1
|
|
@ -56,6 +56,7 @@ The key features are:
|
|||
<a href="https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/?utm_campaign=fastapi_framework&utm_source=fastapi_sponsorship&utm_medium=web_referral" target="_blank" title="Simplify Full Stack Development with FastAPI & MongoDB"><img src="https://fastapi.tiangolo.com/img/sponsors/mongodb.png"></a>
|
||||
<a href="https://zuplo.link/fastapi-gh" target="_blank" title="Zuplo: Scale, Protect, Document, and Monetize your FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/zuplo.png"></a>
|
||||
<a href="https://liblab.com?utm_source=fastapi" target="_blank" title="liblab - Generate SDKs from FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/liblab.png"></a>
|
||||
<a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" target="_blank" title="Deploy & scale any full-stack web app on Render. Focus on building apps, not infra."><img src="https://fastapi.tiangolo.com/img/sponsors/render.svg"></a>
|
||||
<a href="https://github.com/deepset-ai/haystack/" target="_blank" title="Build powerful search from composable, open source building blocks"><img src="https://fastapi.tiangolo.com/img/sponsors/haystack-fastapi.svg"></a>
|
||||
<a href="https://databento.com/" target="_blank" title="Pay as you go for market data"><img src="https://fastapi.tiangolo.com/img/sponsors/databento.svg"></a>
|
||||
<a href="https://speakeasy.com?utm_source=fastapi+repo&utm_medium=github+sponsorship" target="_blank" title="SDKs for your API | Speakeasy"><img src="https://fastapi.tiangolo.com/img/sponsors/speakeasy.png"></a>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ gold:
|
|||
- url: https://liblab.com?utm_source=fastapi
|
||||
title: liblab - Generate SDKs from FastAPI
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/liblab.png
|
||||
- url: https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi
|
||||
title: Deploy & scale any full-stack web app on Render. Focus on building apps, not infra.
|
||||
img: https://fastapi.tiangolo.com/img/sponsors/render.svg
|
||||
silver:
|
||||
- url: https://github.com/deepset-ai/haystack/
|
||||
title: Build powerful search from composable, open source building blocks
|
||||
|
|
|
|||
|
|
@ -31,3 +31,4 @@ logins:
|
|||
- zuplo-oss
|
||||
- Kong
|
||||
- speakeasy-api
|
||||
- jess-render
|
||||
|
|
|
|||
|
|
@ -62,71 +62,7 @@ For OAuth2 they are just strings.
|
|||
|
||||
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:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="5 9 13 47 65 106 108-116 122-125 129-135 140 156"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="2 5 9 13 48 66 107 109-117 123-126 130-136 141 157"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="4 8 12 46 64 105 107-115 121-124 128-134 139 155"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="2 5 9 13 47 65 106 108-116 122-125 129-135 140 156"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:125,129:135,140,156] *}
|
||||
|
||||
Now let's review those changes step by step.
|
||||
|
||||
|
|
@ -136,71 +72,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:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="63-66"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="63-66"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="64-67"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="62-65"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="63-66"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="63-66"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *}
|
||||
|
||||
Because we are now declaring those scopes, they will show up in the API docs when you log-in/authorize.
|
||||
|
||||
|
|
@ -226,71 +98,7 @@ But in your application, for security, you should make sure you only add the sco
|
|||
|
||||
///
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="156"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="156"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="157"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="155"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="156"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="156"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[156] *}
|
||||
|
||||
## Declare scopes in *path operations* and dependencies
|
||||
|
||||
|
|
@ -316,71 +124,7 @@ We are doing it here to demonstrate how **FastAPI** handles scopes declared at d
|
|||
|
||||
///
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="5 140 171"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="5 140 171"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="5 141 172"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="4 139 168"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="5 140 169"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="5 140 169"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,140,171] *}
|
||||
|
||||
/// info | "Technical Details"
|
||||
|
||||
|
|
@ -406,71 +150,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).
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="9 106"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="9 106"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="9 107"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="8 105"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9 106"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="9 106"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
|
||||
|
||||
## Use the `scopes`
|
||||
|
||||
|
|
@ -484,71 +164,7 @@ We create an `HTTPException` that we can reuse (`raise`) later at several points
|
|||
|
||||
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 the `WWW-Authenticate` header (this is part of the spec).
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="106 108-116"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="106 108-116"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="107 109-117"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="105 107-115"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="106 108-116"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="106 108-116"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
|
||||
|
||||
## Verify the `username` and data shape
|
||||
|
||||
|
|
@ -564,71 +180,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.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="47 117-128"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="47 117-128"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="48 118-129"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="46 116-127"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="47 117-128"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="47 117-128"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:128] *}
|
||||
|
||||
## Verify the `scopes`
|
||||
|
||||
|
|
@ -636,71 +188,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`.
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="129-135"
|
||||
{!> ../../docs_src/security/tutorial005_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="129-135"
|
||||
{!> ../../docs_src/security/tutorial005_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="130-136"
|
||||
{!> ../../docs_src/security/tutorial005_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="128-134"
|
||||
{!> ../../docs_src/security/tutorial005_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="129-135"
|
||||
{!> ../../docs_src/security/tutorial005_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip
|
||||
|
||||
Prefer to use the `Annotated` version if possible.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="129-135"
|
||||
{!> ../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
////
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[129:135] *}
|
||||
|
||||
## Dependency tree and scopes
|
||||
|
||||
|
|
|
|||
|
|
@ -15,3 +15,4 @@ You might want to try their services and follow their guides:
|
|||
* <a href="https://docs.platform.sh/languages/python.html?utm_source=fastapi-signup&utm_medium=banner&utm_campaign=FastAPI-signup-June-2023" class="external-link" target="_blank">Platform.sh</a>
|
||||
* <a href="https://docs.porter.run/language-specific-guides/fastapi" class="external-link" target="_blank">Porter</a>
|
||||
* <a href="https://www.withcoherence.com/?utm_medium=advertising&utm_source=fastapi&utm_campaign=website" class="external-link" target="_blank">Coherence</a>
|
||||
* <a href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" class="external-link" target="_blank">Render</a>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -9,6 +9,7 @@ hide:
|
|||
|
||||
### Docs
|
||||
|
||||
* 📝 Update includes in `docs/en/docs/advanced/security/oauth2-scopes.md`. PR [#12572](https://github.com/fastapi/fastapi/pull/12572) by [@krishnamadhavan](https://github.com/krishnamadhavan).
|
||||
* 📝 Update includes for `docs/en/docs/how-to/conditional-openapi.md`. PR [#12624](https://github.com/fastapi/fastapi/pull/12624) by [@rabinlamadong](https://github.com/rabinlamadong).
|
||||
* 📝 Update includes in `docs/en/docs/tutorial/dependencies/index.md`. PR [#12615](https://github.com/fastapi/fastapi/pull/12615) by [@bharara](https://github.com/bharara).
|
||||
* 📝 Update includes in `docs/en/docs/tutorial/response-status-code.md`. PR [#12620](https://github.com/fastapi/fastapi/pull/12620) by [@kantandane](https://github.com/kantandane).
|
||||
|
|
@ -26,11 +27,22 @@ hide:
|
|||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/websockets.md`. PR [#12703](https://github.com/fastapi/fastapi/pull/12703) by [@devfernandoa](https://github.com/devfernandoa).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/security/simple-oauth2.md`. PR [#12520](https://github.com/fastapi/fastapi/pull/12520) by [@LidiaDomingos](https://github.com/LidiaDomingos).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/advanced/response-directly.md`. PR [#12674](https://github.com/fastapi/fastapi/pull/12674) by [@9zimin9](https://github.com/9zimin9).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/middleware.md`. PR [#12704](https://github.com/fastapi/fastapi/pull/12704) by [@devluisrodrigues](https://github.com/devluisrodrigues).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/advanced/openapi-callbacks.md`. PR [#12705](https://github.com/fastapi/fastapi/pull/12705) by [@devfernandoa](https://github.com/devfernandoa).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/request-files.md`. PR [#12706](https://github.com/fastapi/fastapi/pull/12706) by [@devluisrodrigues](https://github.com/devluisrodrigues).
|
||||
* 🌐 Add Portuguese Translation for `docs/pt/docs/advanced/custom-response.md`. PR [#12631](https://github.com/fastapi/fastapi/pull/12631) by [@Joao-Pedro-P-Holanda](https://github.com/Joao-Pedro-P-Holanda).
|
||||
* 🌐 Add Portuguese translation for `docs/pt/docs/tutorial/metadata.md`. PR [#12538](https://github.com/fastapi/fastapi/pull/12538) by [@LinkolnR](https://github.com/LinkolnR).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/metadata.md`. PR [#12541](https://github.com/fastapi/fastapi/pull/12541) by [@kwang1215](https://github.com/kwang1215).
|
||||
* 🌐 Add Korean Translation for `docs/ko/docs/advanced/response-cookies.md`. PR [#12546](https://github.com/fastapi/fastapi/pull/12546) by [@kim-sangah](https://github.com/kim-sangah).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/fastapi-cli.md`. PR [#12515](https://github.com/fastapi/fastapi/pull/12515) by [@dhdld](https://github.com/dhdld).
|
||||
* 🌐 Add Korean Translation for `docs/ko/docs/advanced/response-change-status-code.md`. PR [#12547](https://github.com/fastapi/fastapi/pull/12547) by [@9zimin9](https://github.com/9zimin9).
|
||||
|
||||
### Internal
|
||||
|
||||
* 🔧 Update sponsors: add Render. PR [#12733](https://github.com/fastapi/fastapi/pull/12733) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#12707](https://github.com/fastapi/fastapi/pull/12707) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||
|
||||
## 0.115.4
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@
|
|||
<img class="sponsor-image" src="/img/sponsors/liblab-banner.png" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="item">
|
||||
<a title="Deploy & scale any full-stack web app on Render. Focus on building apps, not infra." style="display: block; position: relative;" href="https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi" target="_blank">
|
||||
<span class="sponsor-badge">sponsor</span>
|
||||
<img class="sponsor-image" src="/img/sponsors/render-banner.svg" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# 응답 쿠키
|
||||
|
||||
## `Response` 매개변수 사용하기
|
||||
|
||||
*경로 작동 함수*에서 `Response` 타입의 매개변수를 선언할 수 있습니다.
|
||||
|
||||
그런 다음 해당 *임시* 응답 객체에서 쿠키를 설정할 수 있습니다.
|
||||
|
||||
```Python hl_lines="1 8-9"
|
||||
{!../../docs_src/response_cookies/tutorial002.py!}
|
||||
```
|
||||
|
||||
그런 다음 필요한 객체(`dict`, 데이터베이스 모델 등)를 반환할 수 있습니다.
|
||||
|
||||
그리고 `response_model`을 선언했다면 반환한 객체를 거르고 변환하는 데 여전히 사용됩니다.
|
||||
|
||||
**FastAPI**는 그 *임시* 응답에서 쿠키(또한 헤더 및 상태 코드)를 추출하고, 반환된 값이 포함된 최종 응답에 이를 넣습니다. 이 값은 `response_model`로 걸러지게 됩니다.
|
||||
|
||||
또한 의존관계에서 `Response` 매개변수를 선언하고, 해당 의존성에서 쿠키(및 헤더)를 설정할 수도 있습니다.
|
||||
|
||||
## `Response`를 직접 반환하기
|
||||
|
||||
코드에서 `Response`를 직접 반환할 때도 쿠키를 생성할 수 있습니다.
|
||||
|
||||
이를 위해 [Response를 직접 반환하기](response-directly.md){.internal-link target=_blank}에서 설명한 대로 응답을 생성할 수 있습니다.
|
||||
|
||||
그런 다음 쿠키를 설정하고 반환하면 됩니다:
|
||||
```Python hl_lines="1 18"
|
||||
{!../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
/// tip
|
||||
|
||||
`Response` 매개변수를 사용하지 않고 응답을 직접 반환하는 경우, FastAPI는 이를 직접 반환한다는 점에 유의하세요.
|
||||
|
||||
따라서 데이터가 올바른 유형인지 확인해야 합니다. 예: `JSONResponse`를 반환하는 경우, JSON과 호환되는지 확인하세요.
|
||||
|
||||
또한 `response_model`로 걸러져야 할 데이터가 전달되지 않도록 확인하세요.
|
||||
|
||||
///
|
||||
|
||||
### 추가 정보
|
||||
|
||||
/// note | "기술적 세부사항"
|
||||
|
||||
`from starlette.responses import Response` 또는 `from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
|
||||
|
||||
**FastAPI**는 개발자의 편의를 위해 `fastapi.responses`로 동일한 `starlette.responses`를 제공합니다. 그러나 대부분의 응답은 Starlette에서 직접 제공됩니다.
|
||||
|
||||
또한 `Response`는 헤더와 쿠키를 설정하는 데 자주 사용되므로, **FastAPI**는 이를 `fastapi.Response`로도 제공합니다.
|
||||
|
||||
///
|
||||
|
||||
사용 가능한 모든 매개변수와 옵션은 <a href="https://www.starlette.io/responses/#set-cookie" class="external-link" target="_blank">Starlette 문서</a>에서 확인할 수 있습니다.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# 응답을 직접 반환하기
|
||||
|
||||
**FastAPI**에서 *경로 작업(path operation)*을 생성할 때, 일반적으로 `dict`, `list`, Pydantic 모델, 데이터베이스 모델 등의 데이터를 반환할 수 있습니다.
|
||||
|
||||
기본적으로 **FastAPI**는 [JSON 호환 가능 인코더](../tutorial/encoder.md){.internal-link target=_blank}에 설명된 `jsonable_encoder`를 사용해 해당 반환 값을 자동으로 `JSON`으로 변환합니다.
|
||||
|
||||
그런 다음, JSON 호환 데이터(예: `dict`)를 `JSONResponse`에 넣어 사용자의 응답을 전송하는 방식으로 처리됩니다.
|
||||
|
||||
그러나 *경로 작업*에서 `JSONResponse`를 직접 반환할 수도 있습니다.
|
||||
|
||||
예를 들어, 사용자 정의 헤더나 쿠키를 반환해야 하는 경우에 유용할 수 있습니다.
|
||||
|
||||
## `Response` 반환하기
|
||||
|
||||
사실, `Response` 또는 그 하위 클래스를 반환할 수 있습니다.
|
||||
|
||||
/// tip
|
||||
|
||||
`JSONResponse` 자체도 `Response`의 하위 클래스입니다.
|
||||
|
||||
///
|
||||
|
||||
그리고 `Response`를 반환하면 **FastAPI**가 이를 그대로 전달합니다.
|
||||
|
||||
Pydantic 모델로 데이터 변환을 수행하지 않으며, 내용을 다른 형식으로 변환하지 않습니다.
|
||||
|
||||
이로 인해 많은 유연성을 얻을 수 있습니다. 어떤 데이터 유형이든 반환할 수 있고, 데이터 선언이나 유효성 검사를 재정의할 수 있습니다.
|
||||
|
||||
## `Response`에서 `jsonable_encoder` 사용하기
|
||||
|
||||
**FastAPI**는 반환하는 `Response`에 아무런 변환을 하지 않으므로, 그 내용이 준비되어 있어야 합니다.
|
||||
|
||||
예를 들어, Pydantic 모델을 `dict`로 변환해 `JSONResponse`에 넣지 않으면 JSON 호환 유형으로 변환된 데이터 유형(예: `datetime`, `UUID` 등)이 사용되지 않습니다.
|
||||
|
||||
이러한 경우, 데이터를 응답에 전달하기 전에 `jsonable_encoder`를 사용하여 변환할 수 있습니다:
|
||||
|
||||
```Python hl_lines="6-7 21-22"
|
||||
{!../../docs_src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// note | "기술적 세부 사항"
|
||||
|
||||
`from starlette.responses import JSONResponse`를 사용할 수도 있습니다.
|
||||
|
||||
**FastAPI**는 개발자의 편의를 위해 `starlette.responses`를 `fastapi.responses`로 제공합니다. 그러나 대부분의 가능한 응답은 Starlette에서 직접 제공합니다.
|
||||
|
||||
///
|
||||
|
||||
## 사용자 정의 `Response` 반환하기
|
||||
위 예제는 필요한 모든 부분을 보여주지만, 아직 유용하지는 않습니다. 사실 데이터를 직접 반환하면 **FastAPI**가 이를 `JSONResponse`에 넣고 `dict`로 변환하는 등 모든 작업을 자동으로 처리합니다.
|
||||
|
||||
이제, 사용자 정의 응답을 반환하는 방법을 알아보겠습니다.
|
||||
|
||||
예를 들어 <a href="https://en.wikipedia.org/wiki/XML" class="external-link" target="_blank">XML</a> 응답을 반환하고 싶다고 가정해보겠습니다.
|
||||
|
||||
XML 내용을 문자열에 넣고, 이를 `Response`에 넣어 반환할 수 있습니다:
|
||||
|
||||
```Python hl_lines="1 18"
|
||||
{!../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
## 참고 사항
|
||||
`Response`를 직접 반환할 때, 그 데이터는 자동으로 유효성 검사되거나, 변환(직렬화)되거나, 문서화되지 않습니다.
|
||||
|
||||
그러나 [OpenAPI에서 추가 응답](additional-responses.md){.internal-link target=_blank}에서 설명된 대로 문서화할 수 있습니다.
|
||||
|
||||
이후 단락에서 자동 데이터 변환, 문서화 등을 사용하면서 사용자 정의 `Response`를 선언하는 방법을 확인할 수 있습니다.
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
# 메타데이터 및 문서화 URL
|
||||
|
||||
**FastAPI** 응용 프로그램에서 다양한 메타데이터 구성을 사용자 맞춤 설정할 수 있습니다.
|
||||
|
||||
## API에 대한 메타데이터
|
||||
|
||||
OpenAPI 명세 및 자동화된 API 문서 UI에 사용되는 다음 필드를 설정할 수 있습니다:
|
||||
|
||||
| 매개변수 | 타입 | 설명 |
|
||||
|----------|------|-------|
|
||||
| `title` | `str` | API의 제목입니다. |
|
||||
| `summary` | `str` | API에 대한 짧은 요약입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능</small> |
|
||||
| `description` | `str` | API에 대한 짧은 설명입니다. 마크다운을 사용할 수 있습니다. |
|
||||
| `version` | `string` | API의 버전입니다. OpenAPI의 버전이 아닌, 여러분의 애플리케이션의 버전을 나타냅니다. 예: `2.5.0` |
|
||||
| `terms_of_service` | `str` | API 이용 약관의 URL입니다. 제공하는 경우 URL 형식이어야 합니다. |
|
||||
| `contact` | `dict` | 노출된 API에 대한 연락처 정보입니다. 여러 필드를 포함할 수 있습니다. <details><summary><code>contact</code> 필드</summary><table><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>연락처 인물/조직의 식별명입니다.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>연락처 정보가 담긴 URL입니다. URL 형식이어야 합니다.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>연락처 인물/조직의 이메일 주소입니다. 이메일 주소 형식이어야 합니다.</td></tr></tbody></table></details> |
|
||||
| `license_info` | `dict` | 노출된 API의 라이선스 정보입니다. 여러 필드를 포함할 수 있습니다. <details><summary><code>license_info</code> 필드</summary><table><thead><tr><th>매개변수</th><th>타입</th><th>설명</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>필수</strong> (<code>license_info</code>가 설정된 경우). API에 사용된 라이선스 이름입니다.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>API에 대한 <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> 라이선스 표현입니다. <code>identifier</code> 필드는 <code>url</code> 필드와 상호 배타적입니다. <small>OpenAPI 3.1.0, FastAPI 0.99.0부터 사용 가능</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>API에 사용된 라이선스의 URL입니다. URL 형식이어야 합니다.</td></tr></tbody></table></details> |
|
||||
|
||||
다음과 같이 설정할 수 있습니다:
|
||||
|
||||
```Python hl_lines="3-16 19-32"
|
||||
{!../../docs_src/metadata/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip
|
||||
|
||||
`description` 필드에 마크다운을 사용할 수 있으며, 출력에서 렌더링됩니다.
|
||||
|
||||
///
|
||||
|
||||
이 구성을 사용하면 문서 자동화(로 생성된) API 문서는 다음과 같이 보입니다:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## 라이선스 식별자
|
||||
|
||||
OpenAPI 3.1.0 및 FastAPI 0.99.0부터 `license_info`에 `identifier`를 URL 대신 설정할 수 있습니다.
|
||||
|
||||
예:
|
||||
|
||||
```Python hl_lines="31"
|
||||
{!../../docs_src/metadata/tutorial001_1.py!}
|
||||
```
|
||||
|
||||
## 태그에 대한 메타데이터
|
||||
|
||||
`openapi_tags` 매개변수를 사용하여 경로 작동을 그룹화하는 데 사용되는 태그에 추가 메타데이터를 추가할 수 있습니다.
|
||||
|
||||
리스트는 각 태그에 대해 하나의 딕셔너리를 포함해야 합니다.
|
||||
|
||||
각 딕셔너리에는 다음이 포함될 수 있습니다:
|
||||
|
||||
* `name` (**필수**): `tags` 매개변수에서 *경로 작동*과 `APIRouter`에 사용된 태그 이름과 동일한 `str`입니다.
|
||||
* `description`: 태그에 대한 간단한 설명을 담은 `str`입니다. 마크다운을 사용할 수 있으며 문서 UI에 표시됩니다.
|
||||
* `externalDocs`: 외부 문서를 설명하는 `dict`이며:
|
||||
* `description`: 외부 문서에 대한 간단한 설명을 담은 `str`입니다.
|
||||
* `url` (**필수**): 외부 문서의 URL을 담은 `str`입니다.
|
||||
|
||||
### 태그에 대한 메타데이터 생성
|
||||
|
||||
`users` 및 `items`에 대한 태그 예시와 함께 메타데이터를 생성하고 이를 `openapi_tags` 매개변수로 전달해 보겠습니다:
|
||||
|
||||
```Python hl_lines="3-16 18"
|
||||
{!../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
설명 안에 마크다운을 사용할 수 있습니다. 예를 들어 "login"은 굵게(**login**) 표시되고, "fancy"는 기울임꼴(_fancy_)로 표시됩니다.
|
||||
|
||||
/// tip
|
||||
|
||||
사용 중인 모든 태그에 메타데이터를 추가할 필요는 없습니다.
|
||||
|
||||
///
|
||||
|
||||
### 태그 사용
|
||||
|
||||
`tags` 매개변수를 *경로 작동* 및 `APIRouter`와 함께 사용하여 태그에 할당할 수 있습니다:
|
||||
|
||||
```Python hl_lines="21 26"
|
||||
{!../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
/// info
|
||||
|
||||
태그에 대한 자세한 내용은 [경로 작동 구성](path-operation-configuration.md#tags){.internal-link target=_blank}에서 읽어보세요.
|
||||
|
||||
///
|
||||
|
||||
### 문서 확인
|
||||
|
||||
이제 문서를 확인하면 모든 추가 메타데이터가 표시됩니다:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### 태그 순서
|
||||
|
||||
각 태그 메타데이터 딕셔너리의 순서는 문서 UI에 표시되는 순서를 정의합니다.
|
||||
|
||||
예를 들어, 알파벳 순서상 `users`는 `items` 뒤에 오지만, 우리는 `users` 메타데이터를 리스트의 첫 번째 딕셔너리로 추가했기 때문에 먼저 표시됩니다.
|
||||
|
||||
## OpenAPI URL
|
||||
|
||||
OpenAPI 구조는 기본적으로 `/openapi.json`에서 제공됩니다.
|
||||
|
||||
`openapi_url` 매개변수를 통해 이를 설정할 수 있습니다.
|
||||
|
||||
예를 들어, 이를 `/api/v1/openapi.json`에 제공하도록 설정하려면:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../docs_src/metadata/tutorial002.py!}
|
||||
```
|
||||
|
||||
OpenAPI 구조를 완전히 비활성화하려면 `openapi_url=None`으로 설정할 수 있으며, 이를 사용하여 문서화 사용자 인터페이스도 비활성화됩니다.
|
||||
|
||||
## 문서화 URL
|
||||
|
||||
포함된 두 가지 문서화 사용자 인터페이스를 설정할 수 있습니다:
|
||||
|
||||
* **Swagger UI**: `/docs`에서 제공됩니다.
|
||||
* `docs_url` 매개변수로 URL을 설정할 수 있습니다.
|
||||
* `docs_url=None`으로 설정하여 비활성화할 수 있습니다.
|
||||
* **ReDoc**: `/redoc`에서 제공됩니다.
|
||||
* `redoc_url` 매개변수로 URL을 설정할 수 있습니다.
|
||||
* `redoc_url=None`으로 설정하여 비활성화할 수 있습니다.
|
||||
|
||||
예를 들어, Swagger UI를 `/documentation`에서 제공하고 ReDoc을 비활성화하려면:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../docs_src/metadata/tutorial003.py!}
|
||||
```
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
# Resposta Personalizada - HTML, Stream, File e outras
|
||||
|
||||
Por padrão, o **FastAPI** irá retornar respostas utilizando `JSONResponse`.
|
||||
|
||||
Mas você pode sobrescrever esse comportamento utilizando `Response` diretamente, como visto em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}.
|
||||
|
||||
Mas se você retornar uma `Response` diretamente (ou qualquer subclasse, como `JSONResponse`), os dados não serão convertidos automaticamente (mesmo que você declare um `response_model`), e a documentação não será gerada automaticamente (por exemplo, incluindo o "media type", no cabeçalho HTTP `Content-Type` como parte do esquema OpenAPI gerado).
|
||||
|
||||
Mas você também pode declarar a `Response` que você deseja utilizar (e.g. qualquer subclasse de `Response`), em um *decorador de operação de rota* utilizando o parâmetro `response_class`.
|
||||
|
||||
Os conteúdos que você retorna em sua *função de operador de rota* serão colocados dentro dessa `Response`.
|
||||
|
||||
E se a `Response` tiver um media type JSON (`application/json`), como é o caso com `JSONResponse` e `UJSONResponse`, os dados que você retornar serão automaticamente convertidos (e filtrados) com qualquer `response_model` do Pydantic que for declarado em sua *função de operador de rota*.
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Se você utilizar uma classe de Resposta sem media type, o FastAPI esperará que sua resposta não tenha conteúdo, então ele não irá documentar o formato da resposta na documentação OpenAPI gerada.
|
||||
|
||||
///
|
||||
|
||||
## Utilizando `ORJSONResponse`
|
||||
|
||||
Por exemplo, se você precisa bastante de performance, você pode instalar e utilizar o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> e definir a resposta para ser uma `ORJSONResponse`.
|
||||
|
||||
Importe a classe, ou subclasse, de `Response` que você deseja utilizar e declare ela no *decorador de operação de rota*.
|
||||
|
||||
Para respostas grandes, retornar uma `Response` diretamente é muito mais rápido que retornar um dicionário.
|
||||
|
||||
Isso ocorre por que, por padrão, o FastAPI irá verificar cada item dentro do dicionário e garantir que ele seja serializável para JSON, utilizando o mesmo[Codificador Compatível com JSON](../tutorial/encoder.md){.internal-link target=_blank} explicado no tutorial. Isso permite que você retorne **objetos abstratos**, como modelos do banco de dados, por exemplo.
|
||||
|
||||
Mas se você tem certeza que o conteúdo que você está retornando é **serializável com JSON**, você pode passá-lo diretamente para a classe de resposta e evitar o trabalho extra que o FastAPI teria ao passar o conteúdo pelo `jsonable_encoder` antes de passar para a classe de resposta.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
{!../../docs_src/custom_response/tutorial001b.py!}
|
||||
```
|
||||
|
||||
/// info | Informação
|
||||
|
||||
O parâmetro `response_class` também será usado para definir o "media type" da resposta.
|
||||
|
||||
Neste caso, o cabeçalho HTTP `Content-Type` irá ser definido como `application/json`.
|
||||
|
||||
E será documentado como tal no OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
A `ORJSONResponse` está disponível apenas no FastAPI, e não no Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Resposta HTML
|
||||
|
||||
Para retornar uma resposta com HTML diretamente do **FastAPI**, utilize `HTMLResponse`.
|
||||
|
||||
* Importe `HTMLResponse`
|
||||
* Passe `HTMLResponse` como o parâmetro de `response_class` do seu *decorador de operação de rota*.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
{!../../docs_src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
/// info | Informação
|
||||
|
||||
O parâmetro `response_class` também será usado para definir o "media type" da resposta.
|
||||
|
||||
Neste caso, o cabeçalho HTTP `Content-Type` será definido como `text/html`.
|
||||
|
||||
E será documentado como tal no OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### Retornando uma `Response`
|
||||
|
||||
Como visto em [Retornando uma Resposta Diretamente](response-directly.md){.internal-link target=_blank}, você também pode sobrescrever a resposta diretamente na sua *operação de rota*, ao retornar ela.
|
||||
|
||||
O mesmo exemplo de antes, retornando uma `HTMLResponse`, poderia parecer com:
|
||||
|
||||
```Python hl_lines="2 7 19"
|
||||
{!../../docs_src/custom_response/tutorial003.py!}
|
||||
```
|
||||
|
||||
/// warning | Aviso
|
||||
|
||||
Uma `Response` retornada diretamente em sua *função de operação de rota* não será documentada no OpenAPI (por exemplo, o `Content-Type` não será documentado) e não será visível na documentação interativa automática.
|
||||
|
||||
///
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Obviamente, o cabeçalho `Content-Type`, o código de status, etc, virão do objeto `Response` que você retornou.
|
||||
|
||||
///
|
||||
|
||||
### Documentar no OpenAPI e sobrescrever `Response`
|
||||
|
||||
Se você deseja sobrescrever a resposta dentro de uma função, mas ao mesmo tempo documentar o "media type" no OpenAPI, você pode utilizar o parâmetro `response_class` E retornar um objeto `Response`.
|
||||
|
||||
A `response_class` será usada apenas para documentar o OpenAPI da *operação de rota*, mas sua `Response` será usada como foi definida.
|
||||
|
||||
##### Retornando uma `HTMLResponse` diretamente
|
||||
|
||||
Por exemplo, poderia ser algo como:
|
||||
|
||||
```Python hl_lines="7 21 23"
|
||||
{!../../docs_src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
Neste exemplo, a função `generate_html_response()` já cria e retorna uma `Response` em vez de retornar o HTML em uma `str`.
|
||||
|
||||
Ao retornar o resultado chamando `generate_html_response()`, você já está retornando uma `Response` que irá sobrescrever o comportamento padrão do **FastAPI**.
|
||||
|
||||
Mas se você passasse uma `HTMLResponse` em `response_class` também, o **FastAPI** saberia como documentar isso no OpenAPI e na documentação interativa como um HTML com `text/html`:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Respostas disponíveis
|
||||
|
||||
Aqui estão algumas dos tipos de resposta disponíveis.
|
||||
|
||||
Lembre-se que você pode utilizar `Response` para retornar qualquer outra coisa, ou até mesmo criar uma subclasse personalizada.
|
||||
|
||||
/// note | Detalhes Técnicos
|
||||
|
||||
Você também pode utilizar `from starlette.responses import HTMLResponse`.
|
||||
|
||||
O **FastAPI** provê a mesma `starlette.responses` como `fastapi.responses` apenas como uma facilidade para você, desenvolvedor. Mas a maioria das respostas disponíveis vêm diretamente do Starlette.
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
|
||||
A classe principal de respostas, todas as outras respostas herdam dela.
|
||||
|
||||
Você pode retorná-la diretamente.
|
||||
|
||||
Ela aceita os seguintes parâmetros:
|
||||
|
||||
* `content` - Uma sequência de caracteres (`str`) ou `bytes`.
|
||||
* `status_code` - Um código de status HTTP do tipo `int`.
|
||||
* `headers` - Um dicionário `dict` de strings.
|
||||
* `media_type` - Uma `str` informando o media type. E.g. `"text/html"`.
|
||||
|
||||
O FastAPI (Starlette, na verdade) irá incluir o cabeçalho Content-Length automaticamente. Ele também irá incluir o cabeçalho Content-Type, baseado no `media_type` e acrescentando uma codificação para tipos textuais.
|
||||
|
||||
```Python hl_lines="1 18"
|
||||
{!../../docs_src/response_directly/tutorial002.py!}
|
||||
```
|
||||
|
||||
### `HTMLResponse`
|
||||
|
||||
Usa algum texto ou sequência de bytes e retorna uma resposta HTML. Como você leu acima.
|
||||
|
||||
### `PlainTextResponse`
|
||||
|
||||
Usa algum texto ou sequência de bytes para retornar uma resposta de texto não formatado.
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!../../docs_src/custom_response/tutorial005.py!}
|
||||
```
|
||||
|
||||
### `JSONResponse`
|
||||
|
||||
Pega alguns dados e retorna uma resposta com codificação `application/json`.
|
||||
|
||||
É a resposta padrão utilizada no **FastAPI**, como você leu acima.
|
||||
|
||||
### `ORJSONResponse`
|
||||
|
||||
Uma alternativa mais rápida de resposta JSON utilizando o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, como você leu acima.
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Essa resposta requer a instalação do pacote `orjson`, com o comando `pip install orjson`, por exemplo.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse`
|
||||
|
||||
Uma alternativa de resposta JSON utilizando a biblioteca <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Essa resposta requer a instalação do pacote `ujson`, com o comando `pip install ujson`, por exemplo.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Aviso
|
||||
|
||||
`ujson` é menos cauteloso que a implementação nativa do Python na forma que os casos especiais são tratados
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
{!../../docs_src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
É possível que `ORJSONResponse` seja uma alternativa mais rápida.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
|
||||
Retorna um redirecionamento HTTP. Utiliza o código de status 307 (Redirecionamento Temporário) por padrão.
|
||||
|
||||
Você pode retornar uma `RedirectResponse` diretamente:
|
||||
|
||||
```Python hl_lines="2 9"
|
||||
{!../../docs_src/custom_response/tutorial006.py!}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Ou você pode utilizá-la no parâmetro `response_class`:
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!../../docs_src/custom_response/tutorial006b.py!}
|
||||
```
|
||||
|
||||
Se você fizer isso, então você pode retornar a URL diretamente da sua *função de operação de rota*
|
||||
|
||||
Neste caso, o `status_code` utilizada será o padrão de `RedirectResponse`, que é `307`.
|
||||
|
||||
---
|
||||
|
||||
Você também pode utilizar o parâmetro `status_code` combinado com o parâmetro `response_class`:
|
||||
|
||||
```Python hl_lines="2 7 9"
|
||||
{!../../docs_src/custom_response/tutorial006c.py!}
|
||||
```
|
||||
|
||||
### `StreamingResponse`
|
||||
|
||||
Recebe uma gerador assíncrono ou um gerador/iterador comum e retorna o corpo da requisição continuamente (stream).
|
||||
|
||||
```Python hl_lines="2 14"
|
||||
{!../../docs_src/custom_response/tutorial007.py!}
|
||||
```
|
||||
|
||||
#### Utilizando `StreamingResponse` com objetos semelhantes a arquivos
|
||||
|
||||
Se você tiver um objeto semelhante a um arquivo (e.g. o objeto retornado por `open()`), você pode criar uma função geradora para iterar sobre esse objeto.
|
||||
|
||||
Dessa forma, você não precisa ler todo o arquivo na memória primeiro, e você pode passar essa função geradora para `StreamingResponse` e retorná-la.
|
||||
|
||||
Isso inclui muitas bibliotecas que interagem com armazenamento em nuvem, processamento de vídeos, entre outras.
|
||||
|
||||
```{ .python .annotate hl_lines="2 10-12 14" }
|
||||
{!../../docs_src/custom_response/tutorial008.py!}
|
||||
```
|
||||
|
||||
1. Essa é a função geradora. É definida como "função geradora" porque contém declarações `yield` nela.
|
||||
2. Ao utilizar o bloco `with`, nós garantimos que o objeto semelhante a um arquivo é fechado após a função geradora ser finalizada. Isto é, após a resposta terminar de ser enivada.
|
||||
3. Essa declaração `yield from` informa a função para iterar sobre essa coisa nomeada de `file_like`. E então, para cada parte iterada, fornece essa parte como se viesse dessa função geradora (`iterfile`).
|
||||
|
||||
Então, é uma função geradora que transfere o trabalho de "geração" para alguma outra coisa interna.
|
||||
|
||||
Fazendo dessa forma, podemos colocá-la em um bloco `with`, e assim garantir que o objeto semelhante a um arquivo é fechado quando a função termina.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Perceba que aqui estamos utilizando o `open()` da biblioteca padrão que não suporta `async` e `await`, e declaramos a operação de rota com o `def` básico.
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
|
||||
Envia um arquivo de forma assíncrona e contínua (stream).
|
||||
*
|
||||
Recebe um conjunto de argumentos do construtor diferente dos outros tipos de resposta:
|
||||
|
||||
* `path` - O caminho do arquivo que será transmitido
|
||||
* `headers` - quaisquer cabeçalhos que serão incluídos, como um dicionário.
|
||||
* `media_type` - Uma string com o media type. Se não for definida, o media type é inferido a partir do nome ou caminho do arquivo.
|
||||
* `filename` - Se for definido, é incluído no cabeçalho `Content-Disposition`.
|
||||
|
||||
Respostas de Arquivos incluem o tamanho do arquivo, data da última modificação e ETags apropriados, nos cabeçalhos `Content-Length`, `Last-Modified` e `ETag`, respectivamente.
|
||||
|
||||
```Python hl_lines="2 10"
|
||||
{!../../docs_src/custom_response/tutorial009.py!}
|
||||
```
|
||||
|
||||
Você também pode usar o parâmetro `response_class`:
|
||||
|
||||
```Python hl_lines="2 8 10"
|
||||
{!../../docs_src/custom_response/tutorial009b.py!}
|
||||
```
|
||||
|
||||
Nesse caso, você pode retornar o caminho do arquivo diretamente da sua *função de operação de rota*.
|
||||
|
||||
## Classe de resposta personalizada
|
||||
|
||||
Você pode criar sua própria classe de resposta, herdando de `Response` e usando essa nova classe.
|
||||
|
||||
Por exemplo, vamos supor que você queira utilizar o <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, mas com algumas configurações personalizadas que não estão incluídas na classe `ORJSONResponse`.
|
||||
|
||||
Vamos supor também que você queira retornar um JSON indentado e formatado, então você quer utilizar a opção `orjson.OPT_INDENT_2` do orjson.
|
||||
|
||||
Você poderia criar uma classe `CustomORJSONResponse`. A principal coisa a ser feita é sobrecarregar o método render da classe Response, `Response.render(content)`, que retorna o conteúdo em bytes, para retornar o conteúdo que você deseja:
|
||||
|
||||
```Python hl_lines="9-14 17"
|
||||
{!../../docs_src/custom_response/tutorial009c.py!}
|
||||
```
|
||||
|
||||
Agora em vez de retornar:
|
||||
|
||||
```json
|
||||
{"message": "Hello World"}
|
||||
```
|
||||
|
||||
...essa resposta retornará:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
Obviamente, você provavelmente vai encontrar maneiras muito melhores de se aproveitar disso do que a formatação de JSON. 😉
|
||||
|
||||
## Classe de resposta padrão
|
||||
|
||||
Quando você criar uma instância da classe **FastAPI** ou um `APIRouter` você pode especificar qual classe de resposta utilizar por padrão.
|
||||
|
||||
O padrão que define isso é o `default_response_class`.
|
||||
|
||||
No exemplo abaixo, o **FastAPI** irá utilizar `ORJSONResponse` por padrão, em todas as *operações de rota*, em vez de `JSONResponse`.
|
||||
|
||||
```Python hl_lines="2 4"
|
||||
{!../../docs_src/custom_response/tutorial010.py!}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Você ainda pode substituir `response_class` em *operações de rota* como antes.
|
||||
|
||||
///
|
||||
|
||||
## Documentação adicional
|
||||
|
||||
Você também pode declarar o media type e muitos outros detalhes no OpenAPI utilizando `responses`: [Retornos Adicionais no OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
# Middleware Avançado
|
||||
|
||||
No tutorial principal você leu como adicionar [Middleware Personalizado](../tutorial/middleware.md){.internal-link target=_blank} à sua aplicação.
|
||||
|
||||
E então você também leu como lidar com [CORS com o `CORSMiddleware`](../tutorial/cors.md){.internal-link target=_blank}.
|
||||
|
||||
Nesta seção, veremos como usar outros middlewares.
|
||||
|
||||
## Adicionando middlewares ASGI
|
||||
|
||||
Como o **FastAPI** é baseado no Starlette e implementa a especificação <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>, você pode usar qualquer middleware ASGI.
|
||||
|
||||
O middleware não precisa ser feito para o FastAPI ou Starlette para funcionar, desde que siga a especificação ASGI.
|
||||
|
||||
No geral, os middlewares ASGI são classes que esperam receber um aplicativo ASGI como o primeiro argumento.
|
||||
|
||||
Então, na documentação de middlewares ASGI de terceiros, eles provavelmente dirão para você fazer algo como:
|
||||
|
||||
```Python
|
||||
from unicorn import UnicornMiddleware
|
||||
|
||||
app = SomeASGIApp()
|
||||
|
||||
new_app = UnicornMiddleware(app, some_config="rainbow")
|
||||
```
|
||||
|
||||
Mas, o FastAPI (na verdade, o Starlette) fornece uma maneira mais simples de fazer isso que garante que os middlewares internos lidem com erros do servidor e que os manipuladores de exceções personalizados funcionem corretamente.
|
||||
|
||||
Para isso, você usa `app.add_middleware()` (como no exemplo para CORS).
|
||||
|
||||
```Python
|
||||
from fastapi import FastAPI
|
||||
from unicorn import UnicornMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
```
|
||||
|
||||
`app.add_middleware()` recebe uma classe de middleware como o primeiro argumento e quaisquer argumentos adicionais a serem passados para o middleware.
|
||||
|
||||
## Middlewares Integrados
|
||||
|
||||
**FastAPI** inclui vários middlewares para casos de uso comuns, veremos a seguir como usá-los.
|
||||
|
||||
/// note | Detalhes Técnicos
|
||||
|
||||
Para o próximo exemplo, você também poderia usar `from starlette.middleware.something import SomethingMiddleware`.
|
||||
|
||||
**FastAPI** fornece vários middlewares em `fastapi.middleware` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria dos middlewares disponíveis vem diretamente do Starlette.
|
||||
|
||||
///
|
||||
|
||||
## `HTTPSRedirectMiddleware`
|
||||
|
||||
Garante que todas as requisições devem ser `https` ou `wss`.
|
||||
|
||||
Qualquer requisição para `http` ou `ws` será redirecionada para o esquema seguro.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
|
||||
Garante que todas as requisições recebidas tenham um cabeçalho `Host` corretamente configurado, a fim de proteger contra ataques de cabeçalho de host HTTP.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
|
||||
|
||||
Os seguintes argumentos são suportados:
|
||||
|
||||
* `allowed_hosts` - Uma lista de nomes de domínio que são permitidos como nomes de host. Domínios com coringa, como `*.example.com`, são suportados para corresponder a subdomínios. Para permitir qualquer nome de host, use `allowed_hosts=["*"]` ou omita o middleware.
|
||||
|
||||
Se uma requisição recebida não for validada corretamente, uma resposta `400` será enviada.
|
||||
|
||||
## `GZipMiddleware`
|
||||
|
||||
Gerencia respostas GZip para qualquer requisição que inclua `"gzip"` no cabeçalho `Accept-Encoding`.
|
||||
|
||||
O middleware lidará com respostas padrão e de streaming.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
|
||||
|
||||
Os seguintes argumentos são suportados:
|
||||
|
||||
* `minimum_size` - Não comprima respostas menores que este tamanho mínimo em bytes. O padrão é `500`.
|
||||
* `compresslevel` - Usado durante a compressão GZip. É um inteiro variando de 1 a 9. O padrão é `9`. Um valor menor resulta em uma compressão mais rápida, mas em arquivos maiores, enquanto um valor maior resulta em uma compressão mais lenta, mas em arquivos menores.
|
||||
|
||||
## Outros middlewares
|
||||
|
||||
Há muitos outros middlewares ASGI.
|
||||
|
||||
Por exemplo:
|
||||
|
||||
* <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>
|
||||
|
||||
Para checar outros middlewares disponíveis, confira <a href="https://www.starlette.io/middleware/" class="external-link" target="_blank">Documentação de Middlewares do Starlette</a> e a <a href="https://github.com/florimondmanca/awesome-asgi" class="external-link" target="_blank">Lista Incrível do ASGI</a>.
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
# Callbacks na OpenAPI
|
||||
|
||||
Você poderia criar uma API com uma *operação de rota* que poderia acionar uma solicitação a uma *API externa* criada por outra pessoa (provavelmente o mesmo desenvolvedor que estaria *usando* sua API).
|
||||
|
||||
O processo que acontece quando seu aplicativo de API chama a *API externa* é chamado de "callback". Porque o software que o desenvolvedor externo escreveu envia uma solicitação para sua API e então sua API *chama de volta*, enviando uma solicitação para uma *API externa* (que provavelmente foi criada pelo mesmo desenvolvedor).
|
||||
|
||||
Nesse caso, você poderia querer documentar como essa API externa *deveria* ser. Que *operação de rota* ela deveria ter, que corpo ela deveria esperar, que resposta ela deveria retornar, etc.
|
||||
|
||||
## Um aplicativo com callbacks
|
||||
|
||||
Vamos ver tudo isso com um exemplo.
|
||||
|
||||
Imagine que você tem um aplicativo que permite criar faturas.
|
||||
|
||||
Essas faturas terão um `id`, `title` (opcional), `customer` e `total`.
|
||||
|
||||
O usuário da sua API (um desenvolvedor externo) criará uma fatura em sua API com uma solicitação POST.
|
||||
|
||||
Então sua API irá (vamos imaginar):
|
||||
|
||||
* Enviar uma solicitação de pagamento para o desenvolvedor externo.
|
||||
* Coletar o dinheiro.
|
||||
* Enviar a notificação de volta para o usuário da API (o desenvolvedor externo).
|
||||
* Isso será feito enviando uma solicitação POST (de *sua API*) para alguma *API externa* fornecida por esse desenvolvedor externo (este é o "callback").
|
||||
|
||||
## O aplicativo **FastAPI** normal
|
||||
|
||||
Vamos primeiro ver como o aplicativo da API normal se pareceria antes de adicionar o callback.
|
||||
|
||||
Ele terá uma *operação de rota* que receberá um corpo `Invoice`, e um parâmetro de consulta `callback_url` que conterá a URL para o callback.
|
||||
|
||||
Essa parte é bastante normal, a maior parte do código provavelmente já é familiar para você:
|
||||
|
||||
```Python hl_lines="9-13 36-53"
|
||||
{!../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
O parâmetro de consulta `callback_url` usa um tipo Pydantic <a href="https://docs.pydantic.dev/latest/api/networks/" class="external-link" target="_blank">Url</a>.
|
||||
|
||||
///
|
||||
|
||||
A única coisa nova é o argumento `callbacks=invoices_callback_router.routes` no decorador da *operação de rota*. Veremos o que é isso a seguir.
|
||||
|
||||
## Documentando o callback
|
||||
|
||||
O código real do callback dependerá muito do seu próprio aplicativo de API.
|
||||
|
||||
E provavelmente variará muito de um aplicativo para o outro.
|
||||
|
||||
Poderia ser apenas uma ou duas linhas de código, como:
|
||||
|
||||
```Python
|
||||
callback_url = "https://example.com/api/v1/invoices/events/"
|
||||
httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
```
|
||||
|
||||
Mas possivelmente a parte mais importante do callback é garantir que o usuário da sua API (o desenvolvedor externo) implemente a *API externa* corretamente, de acordo com os dados que *sua API* vai enviar no corpo da solicitação do callback, etc.
|
||||
|
||||
Então, o que faremos a seguir é adicionar o código para documentar como essa *API externa* deve ser para receber o callback de *sua API*.
|
||||
|
||||
A documentação aparecerá na interface do Swagger em `/docs` em sua API, e permitirá que os desenvolvedores externos saibam como construir a *API externa*.
|
||||
|
||||
Esse exemplo não implementa o callback em si (que poderia ser apenas uma linha de código), apenas a parte da documentação.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
O callback real é apenas uma solicitação HTTP.
|
||||
|
||||
Quando implementando o callback por você mesmo, você pode usar algo como <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a> ou <a href="https://requests.readthedocs.io/" class="external-link" target="_blank">Requisições</a>.
|
||||
|
||||
///
|
||||
|
||||
## Escrevendo o código de documentação do callback
|
||||
|
||||
Esse código não será executado em seu aplicativo, nós só precisamos dele para *documentar* como essa *API externa* deveria ser.
|
||||
|
||||
Mas, você já sabe como criar facilmente documentação automática para uma API com o **FastAPI**.
|
||||
|
||||
Então vamos usar esse mesmo conhecimento para documentar como a *API externa* deveria ser... criando as *operações de rota* que a *API externa* deveria implementar (as que sua API irá chamar).
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Quando escrever o código para documentar um callback, pode ser útil imaginar que você é aquele *desenvolvedor externo*. E que você está atualmente implementando a *API externa*, não *sua API*.
|
||||
|
||||
Adotar temporariamente esse ponto de vista (do *desenvolvedor externo*) pode ajudar a sentir que é mais óbvio onde colocar os parâmetros, o modelo Pydantic para o corpo, para a resposta, etc. para essa *API externa*.
|
||||
|
||||
///
|
||||
|
||||
### Criar um `APIRouter` para o callback
|
||||
|
||||
Primeiramente crie um novo `APIRouter` que conterá um ou mais callbacks.
|
||||
|
||||
```Python hl_lines="3 25"
|
||||
{!../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Crie a *operação de rota* do callback
|
||||
|
||||
Para criar a *operação de rota* do callback, use o mesmo `APIRouter` que você criou acima.
|
||||
|
||||
Ele deve parecer exatamente como uma *operação de rota* normal do FastAPI:
|
||||
|
||||
* Ele provavelmente deveria ter uma declaração do corpo que deveria receber, por exemplo. `body: InvoiceEvent`.
|
||||
* E também deveria ter uma declaração de um código de status de resposta, por exemplo. `response_model=InvoiceEventReceived`.
|
||||
|
||||
```Python hl_lines="16-18 21-22 28-32"
|
||||
{!../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
Há 2 diferenças principais de uma *operação de rota* normal:
|
||||
|
||||
* Ela não necessita ter nenhum código real, porque seu aplicativo nunca chamará esse código. Ele é usado apenas para documentar a *API externa*. Então, a função poderia ter apenas `pass`.
|
||||
* A *rota* pode conter uma <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expressão OpenAPI 3</a> (veja mais abaixo) onde pode usar variáveis com parâmetros e partes da solicitação original enviada para *sua API*.
|
||||
|
||||
### A expressão do caminho do callback
|
||||
|
||||
A *rota* do callback pode ter uma <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expressão OpenAPI 3</a> que pode conter partes da solicitação original enviada para *sua API*.
|
||||
|
||||
Nesse caso, é a `str`:
|
||||
|
||||
```Python
|
||||
"{$callback_url}/invoices/{$request.body.id}"
|
||||
```
|
||||
|
||||
Então, se o usuário da sua API (o desenvolvedor externo) enviar uma solicitação para *sua API* para:
|
||||
|
||||
```
|
||||
https://yourapi.com/invoices/?callback_url=https://www.external.org/events
|
||||
```
|
||||
|
||||
com um corpo JSON de:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"id": "2expen51ve",
|
||||
"customer": "Mr. Richie Rich",
|
||||
"total": "9999"
|
||||
}
|
||||
```
|
||||
|
||||
então *sua API* processará a fatura e, em algum momento posterior, enviará uma solicitação de callback para o `callback_url` (a *API externa*):
|
||||
|
||||
```
|
||||
https://www.external.org/events/invoices/2expen51ve
|
||||
```
|
||||
|
||||
com um corpo JSON contendo algo como:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"description": "Payment celebration",
|
||||
"paid": true
|
||||
}
|
||||
```
|
||||
|
||||
e esperaria uma resposta daquela *API externa* com um corpo JSON como:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Perceba como a URL de callback usada contém a URL recebida como um parâmetro de consulta em `callback_url` (`https://www.external.org/events`) e também o `id` da fatura de dentro do corpo JSON (`2expen51ve`).
|
||||
|
||||
///
|
||||
|
||||
### Adicionar o roteador de callback
|
||||
|
||||
Nesse ponto você tem a(s) *operação de rota de callback* necessária(s) (a(s) que o *desenvolvedor externo* deveria implementar na *API externa*) no roteador de callback que você criou acima.
|
||||
|
||||
Agora use o parâmetro `callbacks` no decorador da *operação de rota de sua API* para passar o atributo `.routes` (que é na verdade apenas uma `list` de rotas/*operações de rota*) do roteador de callback que você criou acima:
|
||||
|
||||
```Python hl_lines="35"
|
||||
{!../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Perceba que você não está passando o roteador em si (`invoices_callback_router`) para `callback=`, mas o atributo `.routes`, como em `invoices_callback_router.routes`.
|
||||
|
||||
///
|
||||
|
||||
### Verifique a documentação
|
||||
|
||||
Agora você pode iniciar seu aplicativo e ir para <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
Você verá sua documentação incluindo uma seção "Callbacks" para sua *operação de rota* que mostra como a *API externa* deveria ser:
|
||||
|
||||
<img src="/img/tutorial/openapi-callbacks/image01.png">
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
# WebSockets
|
||||
|
||||
Você pode usar <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> com **FastAPI**.
|
||||
|
||||
## Instalando `WebSockets`
|
||||
|
||||
Garanta que você criou um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, o ativou e instalou o `websockets`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install websockets
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Cliente WebSockets
|
||||
|
||||
### Em produção
|
||||
|
||||
Em seu sistema de produção, você provavelmente tem um frontend criado com um framework moderno como React, Vue.js ou Angular.
|
||||
|
||||
E para comunicar usando WebSockets com seu backend, você provavelmente usaria as utilidades do seu frontend.
|
||||
|
||||
Ou você pode ter um aplicativo móvel nativo que se comunica diretamente com seu backend WebSocket, em código nativo.
|
||||
|
||||
Ou você pode ter qualquer outra forma de comunicar com o endpoint WebSocket.
|
||||
|
||||
---
|
||||
|
||||
Mas para este exemplo, usaremos um documento HTML muito simples com algum JavaScript, tudo dentro de uma string longa.
|
||||
|
||||
Esse, é claro, não é o ideal e você não o usaria para produção.
|
||||
|
||||
Na produção, você teria uma das opções acima.
|
||||
|
||||
Mas é a maneira mais simples de focar no lado do servidor de WebSockets e ter um exemplo funcional:
|
||||
|
||||
```Python hl_lines="2 6-38 41-43"
|
||||
{!../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Criando um `websocket`
|
||||
|
||||
Em sua aplicação **FastAPI**, crie um `websocket`:
|
||||
|
||||
{*../../docs_src/websockets/tutorial001.py hl[46:47]*}
|
||||
|
||||
/// note | Detalhes Técnicos
|
||||
|
||||
Você também poderia usar `from starlette.websockets import WebSocket`.
|
||||
|
||||
A **FastAPI** fornece o mesmo `WebSocket` diretamente apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente do Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Aguardar por mensagens e enviar mensagens
|
||||
|
||||
Em sua rota WebSocket você pode esperar (`await`) por mensagens e enviar mensagens.
|
||||
|
||||
{*../../docs_src/websockets/tutorial001.py hl[48:52]*}
|
||||
|
||||
Você pode receber e enviar dados binários, de texto e JSON.
|
||||
|
||||
## Tente você mesmo
|
||||
|
||||
Se seu arquivo for nomeado `main.py`, execute sua aplicação com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi dev main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Abra seu navegador em: <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
Você verá uma página simples como:
|
||||
|
||||
<img src="/img/tutorial/websockets/image01.png">
|
||||
|
||||
Você pode digitar mensagens na caixa de entrada e enviá-las:
|
||||
|
||||
<img src="/img/tutorial/websockets/image02.png">
|
||||
|
||||
E sua aplicação **FastAPI** com WebSockets responderá de volta:
|
||||
|
||||
<img src="/img/tutorial/websockets/image03.png">
|
||||
|
||||
Você pode enviar (e receber) muitas mensagens:
|
||||
|
||||
<img src="/img/tutorial/websockets/image04.png">
|
||||
|
||||
E todas elas usarão a mesma conexão WebSocket.
|
||||
|
||||
## Usando `Depends` e outros
|
||||
|
||||
Nos endpoints WebSocket você pode importar do `fastapi` e usar:
|
||||
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
|
||||
Eles funcionam da mesma forma que para outros endpoints FastAPI/*operações de rota*:
|
||||
|
||||
{*../../docs_src/websockets/tutorial002_an_py310.py hl[68:69,82]*}
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Como isso é um WebSocket, não faz muito sentido levantar uma `HTTPException`, em vez disso levantamos uma `WebSocketException`.
|
||||
|
||||
Você pode usar um código de fechamento dos <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1" class="external-link" target="_blank">códigos válidos definidos na especificação</a>.
|
||||
|
||||
///
|
||||
|
||||
### Tente os WebSockets com dependências
|
||||
|
||||
Se seu arquivo for nomeado `main.py`, execute sua aplicação com:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi dev main.py
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Abrar seu browser em: <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
Lá você pode definir:
|
||||
|
||||
* O "Item ID", usado na rota.
|
||||
* O "Token" usado como um parâmetro de consulta.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Perceba que a consulta `token` será manipulada por uma dependência.
|
||||
|
||||
///
|
||||
|
||||
Com isso você pode conectar o WebSocket e então enviar e receber mensagens:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## Lidando com desconexões e múltiplos clientes
|
||||
|
||||
Quando uma conexão WebSocket é fechada, o `await websocket.receive_text()` levantará uma exceção `WebSocketDisconnect`, que você pode então capturar e lidar como neste exemplo.
|
||||
|
||||
{*../../docs_src/websockets/tutorial003_py39.py hl[79:81]*}
|
||||
|
||||
Para testar:
|
||||
|
||||
* Abrar o aplicativo com várias abas do navegador.
|
||||
* Escreva mensagens a partir delas.
|
||||
* Então feche uma das abas.
|
||||
|
||||
Isso levantará a exceção `WebSocketDisconnect`, e todos os outros clientes receberão uma mensagem como:
|
||||
|
||||
```
|
||||
Client #1596980209979 left the chat
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
O app acima é um exemplo mínimo e simples para demonstrar como lidar e transmitir mensagens para várias conexões WebSocket.
|
||||
|
||||
Mas tenha em mente que, como tudo é manipulado na memória, em uma única lista, ele só funcionará enquanto o processo estiver em execução e só funcionará com um único processo.
|
||||
|
||||
Se você precisa de algo fácil de integrar com o FastAPI, mas que seja mais robusto, suportado por Redis, PostgreSQL ou outros, verifique o <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>.
|
||||
|
||||
///
|
||||
|
||||
## Mais informações
|
||||
|
||||
Para aprender mais sobre as opções, verifique a documentação do Starlette para:
|
||||
|
||||
* <a href="https://www.starlette.io/websockets/" class="external-link" target="_blank">A classe `WebSocket`</a>.
|
||||
* <a href="https://www.starlette.io/endpoints/#websocketendpoint" class="external-link" target="_blank">Manipulação de WebSockets baseada em classes</a>.
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
# Metadados e Urls de Documentos
|
||||
|
||||
Você pode personalizar várias configurações de metadados na sua aplicação **FastAPI**.
|
||||
|
||||
## Metadados para API
|
||||
|
||||
Você pode definir os seguintes campos que são usados na especificação OpenAPI e nas interfaces automáticas de documentação da API:
|
||||
|
||||
| Parâmetro | Tipo | Descrição |
|
||||
|------------|------|-------------|
|
||||
| `title` | `str` | O título da API. |
|
||||
| `summary` | `str` | Um breve resumo da API. <small>Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0.</small> |
|
||||
| `description` | `str` | Uma breve descrição da API. Pode usar Markdown. |
|
||||
| `version` | `string` | A versão da API. Esta é a versão da sua aplicação, não do OpenAPI. Por exemplo, `2.5.0`. |
|
||||
| `terms_of_service` | `str` | Uma URL para os Termos de Serviço da API. Se fornecido, deve ser uma URL. |
|
||||
| `contact` | `dict` | As informações de contato da API exposta. Pode conter vários campos. <details><summary>Campos de <code>contact</code></summary><table><thead><tr><th>Parâmetro</th><th>Tipo</th><th>Descrição</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>O nome identificador da pessoa/organização de contato.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>A URL que aponta para as informações de contato. DEVE estar no formato de uma URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>O endereço de e-mail da pessoa/organização de contato. DEVE estar no formato de um endereço de e-mail.</td></tr></tbody></table></details> |
|
||||
| `license_info` | `dict` | As informações de licença para a API exposta. Ela pode conter vários campos. <details><summary>Campos de <code>license_info</code></summary><table><thead><tr><th>Parâmetro</th><th>Tipo</th><th>Descrição</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>OBRIGATÓRIO</strong> (se um <code>license_info</code> for definido). O nome da licença usada para a API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Uma expressão de licença <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> para a API. O campo <code>identifier</code> é mutuamente exclusivo do campo <code>url</code>. <small>Disponível desde OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>Uma URL para a licença usada para a API. DEVE estar no formato de uma URL.</td></tr></tbody></table></details> |
|
||||
|
||||
Você pode defini-los da seguinte maneira:
|
||||
|
||||
```Python hl_lines="3-16 19-32"
|
||||
{!../../docs_src/metadata/tutorial001.py!}
|
||||
```
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Você pode escrever Markdown no campo `description` e ele será renderizado na saída.
|
||||
|
||||
///
|
||||
|
||||
Com essa configuração, a documentação automática da API se pareceria com:
|
||||
|
||||
<img src="/img/tutorial/metadata/image01.png">
|
||||
|
||||
## Identificador de Licença
|
||||
|
||||
Desde o OpenAPI 3.1.0 e FastAPI 0.99.0, você também pode definir o license_info com um identifier em vez de uma url.
|
||||
|
||||
Por exemplo:
|
||||
|
||||
```Python hl_lines="31"
|
||||
{!../../docs_src/metadata/tutorial001_1.py!}
|
||||
```
|
||||
|
||||
## Metadados para tags
|
||||
|
||||
Você também pode adicionar metadados adicionais para as diferentes tags usadas para agrupar suas operações de rota com o parâmetro `openapi_tags`.
|
||||
|
||||
Ele recebe uma lista contendo um dicionário para cada tag.
|
||||
|
||||
Cada dicionário pode conter:
|
||||
|
||||
* `name` (**obrigatório**): uma `str` com o mesmo nome da tag que você usa no parâmetro `tags` nas suas *operações de rota* e `APIRouter`s.
|
||||
* `description`: uma `str` com uma breve descrição da tag. Pode conter Markdown e será exibido na interface de documentação.
|
||||
* `externalDocs`: um `dict` descrevendo a documentação externa com:
|
||||
* `description`: uma `str` com uma breve descrição da documentação externa.
|
||||
* `url` (**obrigatório**): uma `str` com a URL da documentação externa.
|
||||
|
||||
### Criar Metadados para tags
|
||||
|
||||
Vamos tentar isso em um exemplo com tags para `users` e `items`.
|
||||
|
||||
Crie metadados para suas tags e passe-os para o parâmetro `openapi_tags`:
|
||||
|
||||
```Python hl_lines="3-16 18"
|
||||
{!../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
Observe que você pode usar Markdown dentro das descrições. Por exemplo, "login" será exibido em negrito (**login**) e "fancy" será exibido em itálico (_fancy_).
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Você não precisa adicionar metadados para todas as tags que você usa.
|
||||
|
||||
///
|
||||
|
||||
### Use suas tags
|
||||
|
||||
Use o parâmetro `tags` com suas *operações de rota* (e `APIRouter`s) para atribuí-los a diferentes tags:
|
||||
|
||||
```Python hl_lines="21 26"
|
||||
{!../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Leia mais sobre tags em [Configuração de Operação de Caminho](path-operation-configuration.md#tags){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Cheque os documentos
|
||||
|
||||
Agora, se você verificar a documentação, ela exibirá todos os metadados adicionais:
|
||||
|
||||
<img src="/img/tutorial/metadata/image02.png">
|
||||
|
||||
### Ordem das tags
|
||||
|
||||
A ordem de cada dicionário de metadados de tag também define a ordem exibida na interface de documentação.
|
||||
|
||||
Por exemplo, embora `users` apareça após `items` em ordem alfabética, ele é exibido antes deles, porque adicionamos seus metadados como o primeiro dicionário na lista.
|
||||
|
||||
## URL da OpenAPI
|
||||
|
||||
Por padrão, o esquema OpenAPI é servido em `/openapi.json`.
|
||||
|
||||
Mas você pode configurá-lo com o parâmetro `openapi_url`.
|
||||
|
||||
Por exemplo, para defini-lo para ser servido em `/api/v1/openapi.json`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../docs_src/metadata/tutorial002.py!}
|
||||
```
|
||||
|
||||
Se você quiser desativar completamente o esquema OpenAPI, pode definir `openapi_url=None`, o que também desativará as interfaces de documentação que o utilizam.
|
||||
|
||||
## URLs da Documentação
|
||||
|
||||
Você pode configurar as duas interfaces de documentação incluídas:
|
||||
|
||||
* **Swagger UI**: acessível em `/docs`.
|
||||
* Você pode definir sua URL com o parâmetro `docs_url`.
|
||||
* Você pode desativá-la definindo `docs_url=None`.
|
||||
* **ReDoc**: acessível em `/redoc`.
|
||||
* Você pode definir sua URL com o parâmetro `redoc_url`.
|
||||
* Você pode desativá-la definindo `redoc_url=None`.
|
||||
|
||||
Por exemplo, para definir o Swagger UI para ser servido em `/documentation` e desativar o ReDoc:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!../../docs_src/metadata/tutorial003.py!}
|
||||
```
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
# Arquivos de Requisição
|
||||
|
||||
Você pode definir arquivos para serem enviados pelo cliente usando `File`.
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Para receber arquivos enviados, primeiro instale o <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.
|
||||
|
||||
Garanta que você criou um [ambiente virtual](../virtual-environments.md){.internal-link target=_blank}, o ativou e então o instalou, por exemplo:
|
||||
|
||||
```console
|
||||
$ pip install python-multipart
|
||||
```
|
||||
|
||||
Isso é necessário, visto que os arquivos enviados são enviados como "dados de formulário".
|
||||
|
||||
///
|
||||
|
||||
## Importe `File`
|
||||
|
||||
Importe `File` e `UploadFile` de `fastapi`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}
|
||||
|
||||
## Definir Parâmetros `File`
|
||||
|
||||
Crie parâmetros de arquivo da mesma forma que você faria para `Body` ou `Form`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}
|
||||
|
||||
/// info | Informação
|
||||
|
||||
`File` é uma classe que herda diretamente de `Form`.
|
||||
|
||||
Mas lembre-se que quando você importa `Query`, `Path`, `File` e outros de `fastapi`, eles são, na verdade, funções que retornam classes especiais.
|
||||
|
||||
///
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Para declarar corpos de arquivos, você precisa usar `File`, caso contrário, os parâmetros seriam interpretados como parâmetros de consulta ou parâmetros de corpo (JSON).
|
||||
|
||||
///
|
||||
|
||||
Os arquivos serão enviados como "dados de formulário".
|
||||
|
||||
Se você declarar o tipo do parâmetro da função da sua *operação de rota* como `bytes`, o **FastAPI** lerá o arquivo para você e você receberá o conteúdo como `bytes`.
|
||||
|
||||
Mantenha em mente que isso significa que todo o conteúdo será armazenado na memória. Isso funcionará bem para arquivos pequenos.
|
||||
|
||||
Mas há muitos casos em que você pode se beneficiar do uso de `UploadFile`.
|
||||
|
||||
## Parâmetros de Arquivo com `UploadFile`
|
||||
|
||||
Defina um parâmetro de arquivo com um tipo de `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}
|
||||
|
||||
Utilizar `UploadFile` tem várias vantagens sobre `bytes`:
|
||||
|
||||
* Você não precisa utilizar o `File()` no valor padrão do parâmetro.
|
||||
* Ele utiliza um arquivo "spooled":
|
||||
* Um arquivo armazenado na memória até um limite máximo de tamanho, e após passar esse limite, ele será armazenado no disco.
|
||||
* Isso significa que funcionará bem para arquivos grandes como imagens, vídeos, binários grandes, etc., sem consumir toda a memória.
|
||||
* Você pode receber metadados do arquivo enviado.
|
||||
* Ele tem uma <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> interface `assíncrona`.
|
||||
* Ele expõe um objeto python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> que você pode passar diretamente para outras bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
|
||||
|
||||
### `UploadFile`
|
||||
|
||||
`UploadFile` tem os seguintes atributos:
|
||||
|
||||
* `filename`: Uma `str` com o nome do arquivo original que foi enviado (por exemplo, `myimage.jpg`).
|
||||
* `content_type`: Uma `str` com o tipo de conteúdo (tipo MIME / tipo de mídia) (por exemplo, `image/jpeg`).
|
||||
* `file`: Um <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (um <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> objeto). Este é o objeto de arquivo Python que você pode passar diretamente para outras funções ou bibliotecas que esperam um objeto semelhante a um arquivo("file-like").
|
||||
|
||||
`UploadFile` tem os seguintes métodos `assíncronos`. Todos eles chamam os métodos de arquivo correspondentes por baixo dos panos (usando o `SpooledTemporaryFile` interno).
|
||||
|
||||
* `write(data)`: Escreve `data` (`str` ou `bytes`) no arquivo.
|
||||
* `read(size)`: Lê `size` (`int`) bytes/caracteres do arquivo.
|
||||
* `seek(offset)`: Vai para o byte na posição `offset` (`int`) no arquivo.
|
||||
* Por exemplo, `await myfile.seek(0)` irá para o início do arquivo.
|
||||
* Isso é especialmente útil se você executar `await myfile.read()` uma vez e precisar ler o conteúdo novamente.
|
||||
* `close()`: Fecha o arquivo.
|
||||
|
||||
Como todos esses métodos são métodos `assíncronos`, você precisa "aguardar" por eles.
|
||||
|
||||
Por exemplo, dentro de uma função de *operação de rota* `assíncrona`, você pode obter o conteúdo com:
|
||||
|
||||
```Python
|
||||
contents = await myfile.read()
|
||||
```
|
||||
|
||||
Se você estiver dentro de uma função de *operação de rota* normal `def`, você pode acessar o `UploadFile.file` diretamente, por exemplo:
|
||||
|
||||
```Python
|
||||
contents = myfile.file.read()
|
||||
```
|
||||
|
||||
/// note | Detalhes Técnicos do `async`
|
||||
|
||||
Quando você usa os métodos `async`, o **FastAPI** executa os métodos de arquivo em um threadpool e aguarda por eles.
|
||||
|
||||
///
|
||||
|
||||
/// note | "Detalhes Técnicos do Starlette"
|
||||
|
||||
O `UploadFile` do ***FastAPI** herda diretamente do `UploadFile` do **Starlette** , mas adiciona algumas partes necessárias para torná-lo compatível com o **Pydantic** e as outras partes do FastAPI.
|
||||
|
||||
///
|
||||
|
||||
## O que é "Form Data"
|
||||
|
||||
O jeito que os formulários HTML (`<form></form>`) enviam os dados para o servidor normalmente usa uma codificação "especial" para esses dados, a qual é diferente do JSON.
|
||||
|
||||
**FastAPI** se certificará de ler esses dados do lugar certo, ao invés de JSON.
|
||||
|
||||
/// note | "Detalhes Técnicos"
|
||||
|
||||
Dados de formulários normalmente são codificados usando o "media type" (tipo de mídia) `application/x-www-form-urlencoded` quando não incluem arquivos.
|
||||
|
||||
Mas quando o formulário inclui arquivos, ele é codificado como `multipart/form-data`. Se você usar `File`, o **FastAPI** saberá que tem que pegar os arquivos da parte correta do corpo da requisição.
|
||||
|
||||
Se você quiser ler mais sobre essas codificações e campos de formulário, vá para a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> web docs para <code>POST</code></a>.
|
||||
|
||||
///
|
||||
|
||||
/// warning | Aviso
|
||||
|
||||
Você pode declarar múltiplos parâmetros `File` e `Form` em uma *operação de rota*, mas você não pode declarar campos `Body` que você espera receber como JSON, pois a requisição terá o corpo codificado usando `multipart/form-data` ao invés de `application/json`.
|
||||
|
||||
Isso não é uma limitação do **FastAPI**, é parte do protocolo HTTP.
|
||||
|
||||
///
|
||||
|
||||
## Upload de Arquivo Opcional
|
||||
|
||||
Você pode tornar um arquivo opcional usando anotações de tipo padrão e definindo um valor padrão de `None`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}
|
||||
|
||||
## `UploadFile` com Metadados Adicionais
|
||||
|
||||
Você também pode usar `File()` com `UploadFile`, por exemplo, para definir metadados adicionais:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}
|
||||
|
||||
## Uploads de Múltiplos Arquivos
|
||||
|
||||
É possível realizar o upload de vários arquivos ao mesmo tempo.
|
||||
|
||||
Eles serão associados ao mesmo "campo de formulário" enviado usando "dados de formulário".
|
||||
|
||||
Para usar isso, declare uma lista de `bytes` ou `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}
|
||||
|
||||
Você receberá, tal como declarado, uma `list` de `bytes` ou `UploadFile`.
|
||||
|
||||
/// note | "Detalhes Técnicos"
|
||||
|
||||
Você pode também pode usar `from starlette.responses import HTMLResponse`.
|
||||
|
||||
**FastAPI** providencia o mesmo `starlette.responses` que `fastapi.responses` apenas como uma conveniência para você, o desenvolvedor. Mas a maioria das respostas disponíveis vem diretamente do Starlette.
|
||||
|
||||
///
|
||||
|
||||
### Uploads de Múltiplos Arquivos com Metadados Adicionais
|
||||
|
||||
Da mesma forma de antes, você pode usar `File()` para definir parâmetros adicionais, mesmo para `UploadFile`:
|
||||
|
||||
{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}
|
||||
|
||||
## Recapitulando
|
||||
|
||||
Utilize `File`, `bytes` e `UploadFile` para declarar arquivos a serem enviados na requisição, enviados como dados de formulário.
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
# Simples OAuth2 com senha e Bearer
|
||||
|
||||
Agora vamos construir a partir do capítulo anterior e adicionar as partes que faltam para ter um fluxo de segurança completo.
|
||||
|
||||
## Pegue o `username` (nome de usuário) e `password` (senha)
|
||||
|
||||
É utilizado o utils de segurança da **FastAPI** para obter o `username` e a `password`.
|
||||
|
||||
OAuth2 especifica que ao usar o "password flow" (fluxo de senha), que estamos usando, o cliente/usuário deve enviar os campos `username` e `password` como dados do formulário.
|
||||
|
||||
E a especificação diz que os campos devem ser nomeados assim. Portanto, `user-name` ou `email` não funcionariam.
|
||||
|
||||
Mas não se preocupe, você pode mostrá-lo como quiser aos usuários finais no frontend.
|
||||
|
||||
E seus modelos de banco de dados podem usar qualquer outro nome que você desejar.
|
||||
|
||||
Mas para a *operação de rota* de login, precisamos usar esses nomes para serem compatíveis com a especificação (e poder, por exemplo, usar o sistema integrado de documentação da API).
|
||||
|
||||
A especificação também afirma que o `username` e a `password` devem ser enviados como dados de formulário (portanto, não há JSON aqui).
|
||||
|
||||
### `scope`
|
||||
|
||||
A especificação também diz que o cliente pode enviar outro campo de formulário "`scope`" (Escopo).
|
||||
|
||||
O nome do campo do formulário é `scope` (no singular), mas na verdade é uma longa string com "escopos" separados por espaços.
|
||||
|
||||
Cada “scope” é apenas uma string (sem espaços).
|
||||
|
||||
Normalmente são usados para declarar permissões de segurança específicas, por exemplo:
|
||||
|
||||
* `users:read` ou `users:write` são exemplos comuns.
|
||||
* `instagram_basic` é usado pelo Facebook e Instagram.
|
||||
* `https://www.googleapis.com/auth/drive` é usado pelo Google.
|
||||
|
||||
/// info | Informação
|
||||
|
||||
No OAuth2, um "scope" é apenas uma string que declara uma permissão específica necessária.
|
||||
|
||||
Não importa se tem outros caracteres como `:` ou se é uma URL.
|
||||
|
||||
Esses detalhes são específicos da implementação.
|
||||
|
||||
Para OAuth2 são apenas strings.
|
||||
|
||||
///
|
||||
|
||||
## Código para conseguir o `username` e a `password`
|
||||
|
||||
Agora vamos usar os utilitários fornecidos pelo **FastAPI** para lidar com isso.
|
||||
|
||||
### `OAuth2PasswordRequestForm`
|
||||
|
||||
Primeiro, importe `OAuth2PasswordRequestForm` e use-o como uma dependência com `Depends` na *operação de rota* para `/token`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="4 78"
|
||||
{!> ../../docs_src/security/tutorial003_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="4 78"
|
||||
{!> ../../docs_src/security/tutorial003_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="4 79"
|
||||
{!> ../../docs_src/security/tutorial003_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="2 74"
|
||||
{!> ../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="4 76"
|
||||
{!> ../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
`OAuth2PasswordRequestForm` é uma dependência de classe que declara um corpo de formulário com:
|
||||
|
||||
* O `username`.
|
||||
* A `password`.
|
||||
* Um campo `scope` opcional como uma string grande, composta de strings separadas por espaços.
|
||||
* Um `grant_type` (tipo de concessão) opcional.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
A especificação OAuth2 na verdade *requer* um campo `grant_type` com um valor fixo de `password`, mas `OAuth2PasswordRequestForm` não o impõe.
|
||||
|
||||
Se você precisar aplicá-lo, use `OAuth2PasswordRequestFormStrict` em vez de `OAuth2PasswordRequestForm`.
|
||||
|
||||
///
|
||||
|
||||
* Um `client_id` opcional (não precisamos dele em nosso exemplo).
|
||||
* Um `client_secret` opcional (não precisamos dele em nosso exemplo).
|
||||
|
||||
/// info | Informação
|
||||
|
||||
O `OAuth2PasswordRequestForm` não é uma classe especial para **FastAPI** como é `OAuth2PasswordBearer`.
|
||||
|
||||
`OAuth2PasswordBearer` faz com que **FastAPI** saiba que é um esquema de segurança. Portanto, é adicionado dessa forma ao OpenAPI.
|
||||
|
||||
Mas `OAuth2PasswordRequestForm` é apenas uma dependência de classe que você mesmo poderia ter escrito ou poderia ter declarado os parâmetros do `Form` (formulário) diretamente.
|
||||
|
||||
Mas como é um caso de uso comum, ele é fornecido diretamente pelo **FastAPI**, apenas para facilitar.
|
||||
|
||||
///
|
||||
|
||||
### Use os dados do formulário
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
A instância da classe de dependência `OAuth2PasswordRequestForm` não terá um atributo `scope` com a string longa separada por espaços, em vez disso, terá um atributo `scopes` com a lista real de strings para cada escopo enviado.
|
||||
|
||||
Não estamos usando `scopes` neste exemplo, mas a funcionalidade está disponível se você precisar.
|
||||
|
||||
///
|
||||
|
||||
Agora, obtenha os dados do usuário do banco de dados (falso), usando o `username` do campo do formulário.
|
||||
|
||||
Se não existir tal usuário, retornaremos um erro dizendo "Incorrect username or password" (Nome de usuário ou senha incorretos).
|
||||
|
||||
Para o erro, usamos a exceção `HTTPException`:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="3 79-81"
|
||||
{!> ../../docs_src/security/tutorial003_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="3 79-81"
|
||||
{!> ../../docs_src/security/tutorial003_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="3 80-82"
|
||||
{!> ../../docs_src/security/tutorial003_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="1 75-77"
|
||||
{!> ../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="3 77-79"
|
||||
{!> ../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
### Confira a password (senha)
|
||||
|
||||
Neste ponto temos os dados do usuário do nosso banco de dados, mas não verificamos a senha.
|
||||
|
||||
Vamos colocar esses dados primeiro no modelo `UserInDB` do Pydantic.
|
||||
|
||||
Você nunca deve salvar senhas em texto simples, portanto, usaremos o sistema de hashing de senhas (falsas).
|
||||
|
||||
Se as senhas não corresponderem, retornaremos o mesmo erro.
|
||||
|
||||
#### Hashing de senha
|
||||
|
||||
"Hashing" significa: converter algum conteúdo (uma senha neste caso) em uma sequência de bytes (apenas uma string) que parece algo sem sentido.
|
||||
|
||||
Sempre que você passa exatamente o mesmo conteúdo (exatamente a mesma senha), você obtém exatamente a mesma sequência aleatória de caracteres.
|
||||
|
||||
Mas você não pode converter a sequência aleatória de caracteres de volta para a senha.
|
||||
|
||||
##### Porque usar hashing de senha
|
||||
|
||||
Se o seu banco de dados for roubado, o ladrão não terá as senhas em texto simples dos seus usuários, apenas os hashes.
|
||||
|
||||
Assim, o ladrão não poderá tentar usar essas mesmas senhas em outro sistema (como muitos usuários usam a mesma senha em todos os lugares, isso seria perigoso).
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="82-85"
|
||||
{!> ../../docs_src/security/tutorial003_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="82-85"
|
||||
{!> ../../docs_src/security/tutorial003_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="83-86"
|
||||
{!> ../../docs_src/security/tutorial003_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="78-81"
|
||||
{!> ../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="80-83"
|
||||
{!> ../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
#### Sobre `**user_dict`
|
||||
|
||||
`UserInDB(**user_dict)` significa:
|
||||
|
||||
*Passe as keys (chaves) e values (valores) de `user_dict` diretamente como argumentos de valor-chave, equivalente a:*
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
disabled = user_dict["disabled"],
|
||||
hashed_password = user_dict["hashed_password"],
|
||||
)
|
||||
```
|
||||
|
||||
/// info | Informação
|
||||
|
||||
Para uma explicação mais completa de `**user_dict`, verifique [a documentação para **Extra Models**](../extra-models.md#about-user_indict){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
## Retorne o token
|
||||
|
||||
A resposta do endpoint `token` deve ser um objeto JSON.
|
||||
|
||||
Deve ter um `token_type`. No nosso caso, como estamos usando tokens "Bearer", o tipo de token deve ser "`bearer`".
|
||||
|
||||
E deve ter um `access_token`, com uma string contendo nosso token de acesso.
|
||||
|
||||
Para este exemplo simples, seremos completamente inseguros e retornaremos o mesmo `username` do token.
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
No próximo capítulo, você verá uma implementação realmente segura, com hash de senha e tokens <abbr title="JSON Web Tokens">JWT</abbr>.
|
||||
|
||||
Mas, por enquanto, vamos nos concentrar nos detalhes específicos de que precisamos.
|
||||
|
||||
///
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="87"
|
||||
{!> ../../docs_src/security/tutorial003_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="87"
|
||||
{!> ../../docs_src/security/tutorial003_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="88"
|
||||
{!> ../../docs_src/security/tutorial003_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="83"
|
||||
{!> ../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="85"
|
||||
{!> ../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Pela especificação, você deve retornar um JSON com um `access_token` e um `token_type`, o mesmo que neste exemplo.
|
||||
|
||||
Isso é algo que você mesmo deve fazer em seu código e certifique-se de usar essas chaves JSON.
|
||||
|
||||
É quase a única coisa que você deve se lembrar de fazer corretamente, para estar em conformidade com as especificações.
|
||||
|
||||
De resto, **FastAPI** cuida disso para você.
|
||||
|
||||
///
|
||||
|
||||
## Atualize as dependências
|
||||
|
||||
Agora vamos atualizar nossas dependências.
|
||||
|
||||
Queremos obter o `user_user` *somente* se este usuário estiver ativo.
|
||||
|
||||
Portanto, criamos uma dependência adicional `get_current_active_user` que por sua vez usa `get_current_user` como dependência.
|
||||
|
||||
Ambas as dependências retornarão apenas um erro HTTP se o usuário não existir ou se estiver inativo.
|
||||
|
||||
Portanto, em nosso endpoint, só obteremos um usuário se o usuário existir, tiver sido autenticado corretamente e estiver ativo:
|
||||
|
||||
//// tab | Python 3.10+
|
||||
|
||||
```Python hl_lines="58-66 69-74 94"
|
||||
{!> ../../docs_src/security/tutorial003_an_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.9+
|
||||
|
||||
```Python hl_lines="58-66 69-74 94"
|
||||
{!> ../../docs_src/security/tutorial003_an_py39.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+
|
||||
|
||||
```Python hl_lines="59-67 70-75 95"
|
||||
{!> ../../docs_src/security/tutorial003_an.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.10+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="56-64 67-70 88"
|
||||
{!> ../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
//// tab | Python 3.8+ non-Annotated
|
||||
|
||||
/// tip | Dica
|
||||
|
||||
Prefira usar a versão `Annotated`, se possível.
|
||||
|
||||
///
|
||||
|
||||
```Python hl_lines="58-66 69-72 90"
|
||||
{!> ../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
////
|
||||
|
||||
/// info | Informação
|
||||
|
||||
O cabeçalho adicional `WWW-Authenticate` com valor `Bearer` que estamos retornando aqui também faz parte da especificação.
|
||||
|
||||
Qualquer código de status HTTP (erro) 401 "UNAUTHORIZED" também deve retornar um cabeçalho `WWW-Authenticate`.
|
||||
|
||||
No caso de tokens ao portador (nosso caso), o valor desse cabeçalho deve ser `Bearer`.
|
||||
|
||||
Na verdade, você pode pular esse cabeçalho extra e ainda funcionaria.
|
||||
|
||||
Mas é fornecido aqui para estar em conformidade com as especificações.
|
||||
|
||||
Além disso, pode haver ferramentas que esperam e usam isso (agora ou no futuro) e que podem ser úteis para você ou seus usuários, agora ou no futuro.
|
||||
|
||||
Esse é o benefício dos padrões...
|
||||
|
||||
///
|
||||
|
||||
## Veja em ação
|
||||
|
||||
Abra o docs interativo: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
### Autenticação
|
||||
|
||||
Clique no botão "Authorize".
|
||||
|
||||
Use as credenciais:
|
||||
|
||||
User: `johndoe`
|
||||
|
||||
Password: `secret`
|
||||
|
||||
<img src="/img/tutorial/security/image04.png">
|
||||
|
||||
Após autenticar no sistema, você verá assim:
|
||||
|
||||
<img src="/img/tutorial/security/image05.png">
|
||||
|
||||
### Obtenha seus próprios dados de usuário
|
||||
|
||||
Agora use a operação `GET` com o caminho `/users/me`.
|
||||
|
||||
Você obterá os dados do seu usuário, como:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"username": "johndoe",
|
||||
"email": "johndoe@example.com",
|
||||
"full_name": "John Doe",
|
||||
"disabled": false,
|
||||
"hashed_password": "fakehashedsecret"
|
||||
}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/security/image06.png">
|
||||
|
||||
Se você clicar no ícone de cadeado, sair e tentar a mesma operação novamente, receberá um erro HTTP 401 de:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Not authenticated"
|
||||
}
|
||||
```
|
||||
|
||||
### Usuário inativo
|
||||
|
||||
Agora tente com um usuário inativo, autentique-se com:
|
||||
|
||||
User: `alice`
|
||||
|
||||
Password: `secret2`
|
||||
|
||||
E tente usar a operação `GET` com o caminho `/users/me`.
|
||||
|
||||
Você receberá um erro "Usuário inativo", como:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Inactive user"
|
||||
}
|
||||
```
|
||||
|
||||
## Recaptulando
|
||||
|
||||
Agora você tem as ferramentas para implementar um sistema de segurança completo baseado em `username` e `password` para sua API.
|
||||
|
||||
Usando essas ferramentas, você pode tornar o sistema de segurança compatível com qualquer banco de dados e com qualquer usuário ou modelo de dados.
|
||||
|
||||
O único detalhe que falta é que ainda não é realmente "seguro".
|
||||
|
||||
No próximo capítulo você verá como usar uma biblioteca de hashing de senha segura e tokens <abbr title="JSON Web Tokens">JWT</abbr>.
|
||||
Loading…
Reference in New Issue