Sync fastapi docs from 50fa3f7c on 2026-01-11
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Responses Adicionales en OpenAPI
|
||||
# Responses Adicionales en OpenAPI { #additional-responses-in-openapi }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -14,7 +14,7 @@ Esos responses adicionales se incluirán en el esquema de OpenAPI, por lo que ta
|
||||
|
||||
Pero para esos responses adicionales tienes que asegurarte de devolver un `Response` como `JSONResponse` directamente, con tu código de estado y contenido.
|
||||
|
||||
## Response Adicional con `model`
|
||||
## Response Adicional con `model` { #additional-response-with-model }
|
||||
|
||||
Puedes pasar a tus *decoradores de path operation* un parámetro `responses`.
|
||||
|
||||
@@ -26,7 +26,7 @@ Cada uno de esos `dict`s de response puede tener una clave `model`, conteniendo
|
||||
|
||||
Por ejemplo, para declarar otro response con un código de estado `404` y un modelo Pydantic `Message`, puedes escribir:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial001.py hl[18,22] *}
|
||||
{* ../../docs_src/additional_responses/tutorial001_py39.py hl[18,22] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -169,13 +169,13 @@ Los esquemas se referencian a otro lugar dentro del esquema de OpenAPI:
|
||||
}
|
||||
```
|
||||
|
||||
## Media types adicionales para el response principal
|
||||
## Media types adicionales para el response principal { #additional-media-types-for-the-main-response }
|
||||
|
||||
Puedes usar este mismo parámetro `responses` para agregar diferentes media type para el mismo response principal.
|
||||
|
||||
Por ejemplo, puedes agregar un media type adicional de `image/png`, declarando que tu *path operation* puede devolver un objeto JSON (con media type `application/json`) o una imagen PNG:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial002.py hl[19:24,28] *}
|
||||
{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -191,25 +191,25 @@ Pero si has especificado una clase de response personalizada con `None` como su
|
||||
|
||||
///
|
||||
|
||||
## Combinando información
|
||||
## Combinando información { #combining-information }
|
||||
|
||||
También puedes combinar información de response de múltiples lugares, incluyendo los parámetros `response_model`, `status_code`, y `responses`.
|
||||
|
||||
Puedes declarar un `response_model`, usando el código de estado predeterminado `200` (o uno personalizado si lo necesitas), y luego declarar información adicional para ese mismo response en `responses`, directamente en el esquema de OpenAPI.
|
||||
Puedes declarar un `response_model`, usando el código de estado por defecto `200` (o uno personalizado si lo necesitas), y luego declarar información adicional para ese mismo response en `responses`, directamente en el esquema de OpenAPI.
|
||||
|
||||
**FastAPI** manterá la información adicional de `responses` y la combinará con el JSON Schema de tu modelo.
|
||||
**FastAPI** mantendrá la información adicional de `responses` y la combinará con el JSON Schema de tu modelo.
|
||||
|
||||
Por ejemplo, puedes declarar un response con un código de estado `404` que usa un modelo Pydantic y tiene una `description` personalizada.
|
||||
|
||||
Y un response con un código de estado `200` que usa tu `response_model`, pero incluye un `example` personalizado:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial003.py hl[20:31] *}
|
||||
{* ../../docs_src/additional_responses/tutorial003_py39.py hl[20:31] *}
|
||||
|
||||
Todo se combinará e incluirá en tu OpenAPI, y se mostrará en la documentación de la API:
|
||||
|
||||
<img src="/img/tutorial/additional-responses/image01.png">
|
||||
|
||||
## Combina responses predefinidos y personalizados
|
||||
## Combina responses predefinidos y personalizados { #combine-predefined-responses-and-custom-ones }
|
||||
|
||||
Es posible que desees tener algunos responses predefinidos que se apliquen a muchas *path operations*, pero que quieras combinarlos con responses personalizados necesarios por cada *path operation*.
|
||||
|
||||
@@ -237,9 +237,9 @@ Puedes usar esa técnica para reutilizar algunos responses predefinidos en tus *
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/additional_responses/tutorial004.py hl[13:17,26] *}
|
||||
{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *}
|
||||
|
||||
## Más información sobre responses OpenAPI
|
||||
## Más información sobre responses OpenAPI { #more-information-about-openapi-responses }
|
||||
|
||||
Para ver exactamente qué puedes incluir en los responses, puedes revisar estas secciones en la especificación OpenAPI:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Códigos de Estado Adicionales
|
||||
# Códigos de Estado Adicionales { #additional-status-codes }
|
||||
|
||||
Por defecto, **FastAPI** devolverá los responses usando un `JSONResponse`, colocando el contenido que devuelves desde tu *path operation* dentro de ese `JSONResponse`.
|
||||
|
||||
Usará el código de estado por defecto o el que configures en tu *path operation*.
|
||||
|
||||
## Códigos de estado adicionales
|
||||
## Códigos de estado adicionales { #additional-status-codes_1 }
|
||||
|
||||
Si quieres devolver códigos de estado adicionales aparte del principal, puedes hacerlo devolviendo un `Response` directamente, como un `JSONResponse`, y configurando el código de estado adicional directamente.
|
||||
|
||||
@@ -34,7 +34,7 @@ También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
///
|
||||
|
||||
## OpenAPI y documentación de API
|
||||
## OpenAPI y documentación de API { #openapi-and-api-docs }
|
||||
|
||||
Si devuelves códigos de estado adicionales y responses directamente, no se incluirán en el esquema de OpenAPI (la documentación de la API), porque FastAPI no tiene una forma de saber de antemano qué vas a devolver.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Dependencias Avanzadas
|
||||
# Dependencias Avanzadas { #advanced-dependencies }
|
||||
|
||||
## Dependencias con parámetros
|
||||
## Dependencias con parámetros { #parameterized-dependencies }
|
||||
|
||||
Todas las dependencias que hemos visto son una función o clase fija.
|
||||
|
||||
@@ -10,7 +10,7 @@ Imaginemos que queremos tener una dependencia que revise si el parámetro de que
|
||||
|
||||
Pero queremos poder parametrizar ese contenido fijo.
|
||||
|
||||
## Una *instance* "callable"
|
||||
## Una *instance* "callable" { #a-callable-instance }
|
||||
|
||||
En Python hay una forma de hacer que una instance de una clase sea un "callable".
|
||||
|
||||
@@ -22,7 +22,7 @@ Para hacer eso, declaramos un método `__call__`:
|
||||
|
||||
En este caso, este `__call__` es lo que **FastAPI** usará para comprobar parámetros adicionales y sub-dependencias, y es lo que llamará para pasar un valor al parámetro en tu *path operation function* más adelante.
|
||||
|
||||
## Parametrizar la instance
|
||||
## Parametrizar la instance { #parameterize-the-instance }
|
||||
|
||||
Y ahora, podemos usar `__init__` para declarar los parámetros de la instance que podemos usar para "parametrizar" la dependencia:
|
||||
|
||||
@@ -30,7 +30,7 @@ Y ahora, podemos usar `__init__` para declarar los parámetros de la instance qu
|
||||
|
||||
En este caso, **FastAPI** nunca tocará ni se preocupará por `__init__`, lo usaremos directamente en nuestro código.
|
||||
|
||||
## Crear una instance
|
||||
## Crear una instance { #create-an-instance }
|
||||
|
||||
Podríamos crear una instance de esta clase con:
|
||||
|
||||
@@ -38,7 +38,7 @@ Podríamos crear una instance de esta clase con:
|
||||
|
||||
Y de esa manera podemos "parametrizar" nuestra dependencia, que ahora tiene `"bar"` dentro de ella, como el atributo `checker.fixed_content`.
|
||||
|
||||
## Usar la instance como una dependencia
|
||||
## Usar la instance como una dependencia { #use-the-instance-as-a-dependency }
|
||||
|
||||
Luego, podríamos usar este `checker` en un `Depends(checker)`, en lugar de `Depends(FixedContentQueryChecker)`, porque la dependencia es la instance, `checker`, no la clase en sí.
|
||||
|
||||
@@ -63,3 +63,101 @@ En los capítulos sobre seguridad, hay funciones utilitarias que se implementan
|
||||
Si entendiste todo esto, ya sabes cómo funcionan por debajo esas herramientas de utilidad para seguridad.
|
||||
|
||||
///
|
||||
|
||||
## Dependencias con `yield`, `HTTPException`, `except` y Tareas en segundo plano { #dependencies-with-yield-httpexception-except-and-background-tasks }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Muy probablemente no necesites estos detalles técnicos.
|
||||
|
||||
Estos detalles son útiles principalmente si tenías una aplicación de FastAPI anterior a la 0.121.0 y estás enfrentando problemas con dependencias con `yield`.
|
||||
|
||||
///
|
||||
|
||||
Las dependencias con `yield` han evolucionado con el tiempo para cubrir diferentes casos de uso y arreglar algunos problemas; aquí tienes un resumen de lo que ha cambiado.
|
||||
|
||||
### Dependencias con `yield` y `scope` { #dependencies-with-yield-and-scope }
|
||||
|
||||
En la versión 0.121.0, FastAPI agregó soporte para `Depends(scope="function")` para dependencias con `yield`.
|
||||
|
||||
Usando `Depends(scope="function")`, el código de salida después de `yield` se ejecuta justo después de que la *path operation function* termina, antes de que la response se envíe de vuelta al cliente.
|
||||
|
||||
Y al usar `Depends(scope="request")` (el valor por defecto), el código de salida después de `yield` se ejecuta después de que la response es enviada.
|
||||
|
||||
Puedes leer más al respecto en la documentación de [Dependencias con `yield` - Salida temprana y `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope).
|
||||
|
||||
### Dependencias con `yield` y `StreamingResponse`, detalles técnicos { #dependencies-with-yield-and-streamingresponse-technical-details }
|
||||
|
||||
Antes de FastAPI 0.118.0, si usabas una dependencia con `yield`, ejecutaba el código de salida después de que la *path operation function* retornaba pero justo antes de enviar la response.
|
||||
|
||||
La intención era evitar retener recursos por más tiempo del necesario, esperando a que la response viajara por la red.
|
||||
|
||||
Este cambio también significaba que si retornabas un `StreamingResponse`, el código de salida de la dependencia con `yield` ya se habría ejecutado.
|
||||
|
||||
Por ejemplo, si tenías una sesión de base de datos en una dependencia con `yield`, el `StreamingResponse` no podría usar esa sesión mientras hace streaming de datos porque la sesión ya se habría cerrado en el código de salida después de `yield`.
|
||||
|
||||
Este comportamiento se revirtió en la 0.118.0, para hacer que el código de salida después de `yield` se ejecute después de que la response sea enviada.
|
||||
|
||||
/// info | Información
|
||||
|
||||
Como verás abajo, esto es muy similar al comportamiento anterior a la versión 0.106.0, pero con varias mejoras y arreglos de bugs para casos límite.
|
||||
|
||||
///
|
||||
|
||||
#### Casos de uso con salida temprana del código { #use-cases-with-early-exit-code }
|
||||
|
||||
Hay algunos casos de uso con condiciones específicas que podrían beneficiarse del comportamiento antiguo de ejecutar el código de salida de dependencias con `yield` antes de enviar la response.
|
||||
|
||||
Por ejemplo, imagina que tienes código que usa una sesión de base de datos en una dependencia con `yield` solo para verificar un usuario, pero la sesión de base de datos no se vuelve a usar en la *path operation function*, solo en la dependencia, y la response tarda mucho en enviarse, como un `StreamingResponse` que envía datos lentamente, pero que por alguna razón no usa la base de datos.
|
||||
|
||||
En este caso, la sesión de base de datos se mantendría hasta que la response termine de enviarse, pero si no la usas, entonces no sería necesario mantenerla.
|
||||
|
||||
Así es como se vería:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py *}
|
||||
|
||||
El código de salida, el cierre automático de la `Session` en:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *}
|
||||
|
||||
...se ejecutaría después de que la response termine de enviar los datos lentos:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *}
|
||||
|
||||
Pero como `generate_stream()` no usa la sesión de base de datos, no es realmente necesario mantener la sesión abierta mientras se envía la response.
|
||||
|
||||
Si tienes este caso de uso específico usando SQLModel (o SQLAlchemy), podrías cerrar explícitamente la sesión después de que ya no la necesites:
|
||||
|
||||
{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *}
|
||||
|
||||
De esa manera la sesión liberaría la conexión a la base de datos, para que otras requests puedan usarla.
|
||||
|
||||
Si tienes un caso de uso diferente que necesite salir temprano desde una dependencia con `yield`, por favor crea una <a href="https://github.com/fastapi/fastapi/discussions/new?category=questions" class="external-link" target="_blank">Pregunta de Discusión en GitHub</a> con tu caso de uso específico y por qué te beneficiaría tener cierre temprano para dependencias con `yield`.
|
||||
|
||||
Si hay casos de uso convincentes para el cierre temprano en dependencias con `yield`, consideraría agregar una nueva forma de optar por el cierre temprano.
|
||||
|
||||
### Dependencias con `yield` y `except`, detalles técnicos { #dependencies-with-yield-and-except-technical-details }
|
||||
|
||||
Antes de FastAPI 0.110.0, si usabas una dependencia con `yield`, y luego capturabas una excepción con `except` en esa dependencia, y no volvías a elevar la excepción, la excepción se elevaría/remitiría automáticamente a cualquier manejador de excepciones o al manejador de error interno del servidor.
|
||||
|
||||
Esto cambió en la versión 0.110.0 para arreglar consumo de memoria no manejado por excepciones reenviadas sin un manejador (errores internos del servidor), y para hacerlo consistente con el comportamiento del código Python normal.
|
||||
|
||||
### Tareas en segundo plano y dependencias con `yield`, detalles técnicos { #background-tasks-and-dependencies-with-yield-technical-details }
|
||||
|
||||
Antes de FastAPI 0.106.0, elevar excepciones después de `yield` no era posible, el código de salida en dependencias con `yield` se ejecutaba después de que la response era enviada, por lo que [Manejadores de Excepciones](../tutorial/handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} ya habrían corrido.
|
||||
|
||||
Esto se diseñó así principalmente para permitir usar los mismos objetos devueltos con `yield` por las dependencias dentro de tareas en segundo plano, porque el código de salida se ejecutaría después de que las tareas en segundo plano terminaran.
|
||||
|
||||
Esto cambió en FastAPI 0.106.0 con la intención de no retener recursos mientras se espera a que la response viaje por la red.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Adicionalmente, una tarea en segundo plano normalmente es un conjunto independiente de lógica que debería manejarse por separado, con sus propios recursos (por ejemplo, su propia conexión a la base de datos).
|
||||
|
||||
Así, probablemente tendrás un código más limpio.
|
||||
|
||||
///
|
||||
|
||||
Si solías depender de este comportamiento, ahora deberías crear los recursos para las tareas en segundo plano dentro de la propia tarea en segundo plano, y usar internamente solo datos que no dependan de los recursos de dependencias con `yield`.
|
||||
|
||||
Por ejemplo, en lugar de usar la misma sesión de base de datos, crearías una nueva sesión de base de datos dentro de la tarea en segundo plano, y obtendrías los objetos de la base de datos usando esta nueva sesión. Y entonces, en lugar de pasar el objeto de la base de datos como parámetro a la función de la tarea en segundo plano, pasarías el ID de ese objeto y luego obtendrías el objeto de nuevo dentro de la función de la tarea en segundo plano.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tests Asíncronos
|
||||
# Tests Asíncronos { #async-tests }
|
||||
|
||||
Ya has visto cómo probar tus aplicaciones de **FastAPI** usando el `TestClient` proporcionado. Hasta ahora, solo has visto cómo escribir tests sincrónicos, sin usar funciones `async`.
|
||||
|
||||
@@ -6,11 +6,11 @@ Poder usar funciones asíncronas en tus tests puede ser útil, por ejemplo, cuan
|
||||
|
||||
Veamos cómo podemos hacer que esto funcione.
|
||||
|
||||
## pytest.mark.anyio
|
||||
## pytest.mark.anyio { #pytest-mark-anyio }
|
||||
|
||||
Si queremos llamar funciones asíncronas en nuestros tests, nuestras funciones de test tienen que ser asíncronas. AnyIO proporciona un plugin útil para esto, que nos permite especificar que algunas funciones de test deben ser llamadas de manera asíncrona.
|
||||
|
||||
## HTTPX
|
||||
## HTTPX { #httpx }
|
||||
|
||||
Incluso si tu aplicación de **FastAPI** usa funciones `def` normales en lugar de `async def`, sigue siendo una aplicación `async` por debajo.
|
||||
|
||||
@@ -18,7 +18,7 @@ El `TestClient` hace algo de magia interna para llamar a la aplicación FastAPI
|
||||
|
||||
El `TestClient` está basado en <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>, y afortunadamente, podemos usarlo directamente para probar la API.
|
||||
|
||||
## Ejemplo
|
||||
## Ejemplo { #example }
|
||||
|
||||
Para un ejemplo simple, consideremos una estructura de archivos similar a la descrita en [Aplicaciones Más Grandes](../tutorial/bigger-applications.md){.internal-link target=_blank} y [Testing](../tutorial/testing.md){.internal-link target=_blank}:
|
||||
|
||||
@@ -32,13 +32,13 @@ Para un ejemplo simple, consideremos una estructura de archivos similar a la des
|
||||
|
||||
El archivo `main.py` tendría:
|
||||
|
||||
{* ../../docs_src/async_tests/main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/main.py *}
|
||||
|
||||
El archivo `test_main.py` tendría los tests para `main.py`, podría verse así ahora:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py *}
|
||||
|
||||
## Ejecútalo
|
||||
## Ejecútalo { #run-it }
|
||||
|
||||
Puedes ejecutar tus tests como de costumbre vía:
|
||||
|
||||
@@ -52,21 +52,21 @@ $ pytest
|
||||
|
||||
</div>
|
||||
|
||||
## En Detalle
|
||||
## En Detalle { #in-detail }
|
||||
|
||||
El marcador `@pytest.mark.anyio` le dice a pytest que esta función de test debe ser llamada asíncronamente:
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[7] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Note que la función de test ahora es `async def` en lugar de solo `def` como antes al usar el `TestClient`.
|
||||
Nota que la función de test ahora es `async def` en lugar de solo `def` como antes al usar el `TestClient`.
|
||||
|
||||
///
|
||||
|
||||
Luego podemos crear un `AsyncClient` con la app y enviar requests asíncronos a ella, usando `await`.
|
||||
|
||||
{* ../../docs_src/async_tests/test_main.py hl[9:12] *}
|
||||
{* ../../docs_src/async_tests/app_a_py39/test_main.py hl[9:12] *}
|
||||
|
||||
Esto es equivalente a:
|
||||
|
||||
@@ -88,12 +88,12 @@ Si tu aplicación depende de eventos de lifespan, el `AsyncClient` no activará
|
||||
|
||||
///
|
||||
|
||||
## Otras Llamadas a Funciones Asíncronas
|
||||
## Otras Llamadas a Funciones Asíncronas { #other-asynchronous-function-calls }
|
||||
|
||||
Al ser la función de test asíncrona, ahora también puedes llamar (y `await`) otras funciones `async` además de enviar requests a tu aplicación FastAPI en tus tests, exactamente como las llamarías en cualquier otro lugar de tu código.
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si encuentras un `RuntimeError: Task attached to a different loop` al integrar llamadas a funciones asíncronas en tus tests (por ejemplo, cuando usas <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), recuerda crear instances de objetos que necesiten un loop de eventos solo dentro de funciones async, por ejemplo, en un callback `'@app.on_event("startup")`.
|
||||
Si encuentras un `RuntimeError: Task attached to a different loop` al integrar llamadas a funciones asíncronas en tus tests (por ejemplo, cuando usas <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MotorClient de MongoDB</a>), recuerda crear instances de objetos que necesiten un loop de eventos solo dentro de funciones async, por ejemplo, en un callback `@app.on_event("startup")`.
|
||||
|
||||
///
|
||||
|
||||
@@ -1,6 +1,105 @@
|
||||
# Detrás de un Proxy
|
||||
# Detrás de un Proxy { #behind-a-proxy }
|
||||
|
||||
En algunas situaciones, podrías necesitar usar un **proxy** como Traefik o Nginx con una configuración que añade un prefijo de path extra que no es visto por tu aplicación.
|
||||
En muchas situaciones, usarías un **proxy** como Traefik o Nginx delante de tu app de FastAPI.
|
||||
|
||||
Estos proxies podrían manejar certificados HTTPS y otras cosas.
|
||||
|
||||
## Headers reenviados por el Proxy { #proxy-forwarded-headers }
|
||||
|
||||
Un **proxy** delante de tu aplicación normalmente establecería algunos headers sobre la marcha antes de enviar los requests a tu **server** para que el servidor sepa que el request fue **reenviado** por el proxy, informándole la URL original (pública), incluyendo el dominio, que está usando HTTPS, etc.
|
||||
|
||||
El programa **server** (por ejemplo **Uvicorn** a través de **FastAPI CLI**) es capaz de interpretar esos headers, y luego pasar esa información a tu aplicación.
|
||||
|
||||
Pero por seguridad, como el server no sabe que está detrás de un proxy confiable, no interpretará esos headers.
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
Los headers del proxy son:
|
||||
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
|
||||
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
|
||||
|
||||
///
|
||||
|
||||
### Habilitar headers reenviados por el Proxy { #enable-proxy-forwarded-headers }
|
||||
|
||||
Puedes iniciar FastAPI CLI con la *Opción de CLI* `--forwarded-allow-ips` y pasar las direcciones IP que deberían ser confiables para leer esos headers reenviados.
|
||||
|
||||
Si lo estableces a `--forwarded-allow-ips="*"`, confiaría en todas las IPs entrantes.
|
||||
|
||||
Si tu **server** está detrás de un **proxy** confiable y solo el proxy le habla, esto haría que acepte cualquiera que sea la IP de ese **proxy**.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run --forwarded-allow-ips="*"
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Redirecciones con HTTPS { #redirects-with-https }
|
||||
|
||||
Por ejemplo, digamos que defines una *path operation* `/items/`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_01_py39.py hl[6] *}
|
||||
|
||||
Si el cliente intenta ir a `/items`, por defecto, sería redirigido a `/items/`.
|
||||
|
||||
Pero antes de configurar la *Opción de CLI* `--forwarded-allow-ips` podría redirigir a `http://localhost:8000/items/`.
|
||||
|
||||
Pero quizá tu aplicación está alojada en `https://mysuperapp.com`, y la redirección debería ser a `https://mysuperapp.com/items/`.
|
||||
|
||||
Al configurar `--proxy-headers` ahora FastAPI podrá redirigir a la ubicación correcta. 😎
|
||||
|
||||
```
|
||||
https://mysuperapp.com/items/
|
||||
```
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Si quieres aprender más sobre HTTPS, revisa la guía [Acerca de HTTPS](../deployment/https.md){.internal-link target=_blank}.
|
||||
|
||||
///
|
||||
|
||||
### Cómo funcionan los headers reenviados por el Proxy { #how-proxy-forwarded-headers-work }
|
||||
|
||||
Aquí tienes una representación visual de cómo el **proxy** añade headers reenviados entre el cliente y el **application server**:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Cliente
|
||||
participant Proxy as Proxy/Load Balancer
|
||||
participant Server as Servidor de FastAPI
|
||||
|
||||
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Proxy: El proxy añade headers reenviados
|
||||
|
||||
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
|
||||
|
||||
Note over Server: El servidor interpreta los headers<br/>(si --forwarded-allow-ips está configurado)
|
||||
|
||||
Server->>Proxy: HTTP Response<br/>con URLs HTTPS correctas
|
||||
|
||||
Proxy->>Client: HTTPS Response
|
||||
```
|
||||
|
||||
El **proxy** intercepta el request original del cliente y añade los *headers* especiales de reenvío (`X-Forwarded-*`) antes de pasar el request al **application server**.
|
||||
|
||||
Estos headers preservan información sobre el request original que de otro modo se perdería:
|
||||
|
||||
* **X-Forwarded-For**: La IP original del cliente
|
||||
* **X-Forwarded-Proto**: El protocolo original (`https`)
|
||||
* **X-Forwarded-Host**: El host original (`mysuperapp.com`)
|
||||
|
||||
Cuando **FastAPI CLI** está configurado con `--forwarded-allow-ips`, confía en estos headers y los usa, por ejemplo para generar las URLs correctas en redirecciones.
|
||||
|
||||
## Proxy con un prefijo de path eliminado { #proxy-with-a-stripped-path-prefix }
|
||||
|
||||
Podrías tener un proxy que añada un prefijo de path a tu aplicación.
|
||||
|
||||
En estos casos, puedes usar `root_path` para configurar tu aplicación.
|
||||
|
||||
@@ -10,15 +109,13 @@ El `root_path` se usa para manejar estos casos específicos.
|
||||
|
||||
Y también se usa internamente al montar subaplicaciones.
|
||||
|
||||
## Proxy con un prefijo de path eliminado
|
||||
|
||||
Tener un proxy con un prefijo de path eliminado, en este caso, significa que podrías declarar un path en `/app` en tu código, pero luego añades una capa encima (el proxy) que situaría tu aplicación **FastAPI** bajo un path como `/api/v1`.
|
||||
|
||||
En este caso, el path original `/app` realmente sería servido en `/api/v1/app`.
|
||||
|
||||
Aunque todo tu código esté escrito asumiendo que solo existe `/app`.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[6] *}
|
||||
|
||||
Y el proxy estaría **"eliminando"** el **prefijo del path** sobre la marcha antes de transmitir el request al servidor de aplicaciones (probablemente Uvicorn a través de FastAPI CLI), manteniendo a tu aplicación convencida de que está siendo servida en `/app`, así que no tienes que actualizar todo tu código para incluir el prefijo `/api/v1`.
|
||||
|
||||
@@ -66,14 +163,14 @@ La UI de los docs también necesitaría el esquema de OpenAPI para declarar que
|
||||
|
||||
En este ejemplo, el "Proxy" podría ser algo como **Traefik**. Y el servidor sería algo como FastAPI CLI con **Uvicorn**, ejecutando tu aplicación de FastAPI.
|
||||
|
||||
### Proporcionando el `root_path`
|
||||
### Proporcionando el `root_path` { #providing-the-root-path }
|
||||
|
||||
Para lograr esto, puedes usar la opción de línea de comandos `--root-path` como:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
@@ -90,20 +187,20 @@ Y la opción de línea de comandos `--root-path` proporciona ese `root_path`.
|
||||
|
||||
///
|
||||
|
||||
### Revisar el `root_path` actual
|
||||
### Revisar el `root_path` actual { #checking-the-current-root-path }
|
||||
|
||||
Puedes obtener el `root_path` actual utilizado por tu aplicación para cada request, es parte del diccionario `scope` (que es parte de la especificación ASGI).
|
||||
|
||||
Aquí lo estamos incluyendo en el mensaje solo con fines de demostración.
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial001_py39.py hl[8] *}
|
||||
|
||||
Luego, si inicias Uvicorn con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
@@ -119,19 +216,19 @@ El response sería algo como:
|
||||
}
|
||||
```
|
||||
|
||||
### Configurar el `root_path` en la app de FastAPI
|
||||
### Configurar el `root_path` en la app de FastAPI { #setting-the-root-path-in-the-fastapi-app }
|
||||
|
||||
Alternativamente, si no tienes una forma de proporcionar una opción de línea de comandos como `--root-path` o su equivalente, puedes configurar el parámetro `root_path` al crear tu app de FastAPI:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002.py hl[3] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial002_py39.py hl[3] *}
|
||||
|
||||
Pasar el `root_path` a `FastAPI` sería el equivalente a pasar la opción de línea de comandos `--root-path` a Uvicorn o Hypercorn.
|
||||
|
||||
### Acerca de `root_path`
|
||||
### Acerca de `root_path` { #about-root-path }
|
||||
|
||||
Ten en cuenta que el servidor (Uvicorn) no usará ese `root_path` para nada, a excepción de pasárselo a la app.
|
||||
|
||||
Pero si vas con tu navegador a <a href="http://127.0.0.1:8000" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> verás el response normal:
|
||||
Pero si vas con tu navegador a <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> verás el response normal:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -144,15 +241,15 @@ Así que no se esperará que sea accedido en `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
Uvicorn esperará que el proxy acceda a Uvicorn en `http://127.0.0.1:8000/app`, y luego será responsabilidad del proxy añadir el prefijo extra `/api/v1` encima.
|
||||
|
||||
## Sobre proxies con un prefijo de path eliminado
|
||||
## Sobre proxies con un prefijo de path eliminado { #about-proxies-with-a-stripped-path-prefix }
|
||||
|
||||
Ten en cuenta que un proxy con prefijo de path eliminado es solo una de las formas de configurarlo.
|
||||
|
||||
Probablemente en muchos casos, el valor predeterminado será que el proxy no tenga un prefijo de path eliminado.
|
||||
Probablemente en muchos casos, el valor por defecto será que el proxy no tenga un prefijo de path eliminado.
|
||||
|
||||
En un caso así (sin un prefijo de path eliminado), el proxy escucharía algo como `https://myawesomeapp.com`, y luego si el navegador va a `https://myawesomeapp.com/api/v1/app` y tu servidor (por ejemplo, Uvicorn) escucha en `http://127.0.0.1:8000`, el proxy (sin un prefijo de path eliminado) accedería a Uvicorn en el mismo path: `http://127.0.0.1:8000/api/v1/app`.
|
||||
|
||||
## Probando localmente con Traefik
|
||||
## Probando localmente con Traefik { #testing-locally-with-traefik }
|
||||
|
||||
Puedes ejecutar fácilmente el experimento localmente con un prefijo de path eliminado usando <a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a>.
|
||||
|
||||
@@ -224,14 +321,14 @@ Y ahora inicia tu app, utilizando la opción `--root-path`:
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ fastapi run main.py --root-path /api/v1
|
||||
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
|
||||
|
||||
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Revisa los responses
|
||||
### Revisa los responses { #check-the-responses }
|
||||
|
||||
Ahora, si vas a la URL con el puerto para Uvicorn: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, verás el response normal:
|
||||
|
||||
@@ -267,7 +364,7 @@ Y la versión sin el prefijo de path (`http://127.0.0.1:8000/app`), proporcionad
|
||||
|
||||
Eso demuestra cómo el Proxy (Traefik) usa el prefijo de path y cómo el servidor (Uvicorn) usa el `root_path` de la opción `--root-path`.
|
||||
|
||||
### Revisa la UI de los docs
|
||||
### Revisa la UI de los docs { #check-the-docs-ui }
|
||||
|
||||
Pero aquí está la parte divertida. ✨
|
||||
|
||||
@@ -287,7 +384,7 @@ Justo como queríamos. ✔️
|
||||
|
||||
Esto es porque FastAPI usa este `root_path` para crear el `server` por defecto en OpenAPI con la URL proporcionada por `root_path`.
|
||||
|
||||
## Servidores adicionales
|
||||
## Servidores adicionales { #additional-servers }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -303,7 +400,7 @@ Si pasas una lista personalizada de `servers` y hay un `root_path` (porque tu AP
|
||||
|
||||
Por ejemplo:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003.py hl[4:7] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial003_py39.py hl[4:7] *}
|
||||
|
||||
Generará un esquema de OpenAPI como:
|
||||
|
||||
@@ -317,11 +414,11 @@ Generará un esquema de OpenAPI como:
|
||||
},
|
||||
{
|
||||
"url": "https://stag.example.com",
|
||||
"description": "Entorno de pruebas"
|
||||
"description": "Staging environment"
|
||||
},
|
||||
{
|
||||
"url": "https://prod.example.com",
|
||||
"description": "Entorno de producción"
|
||||
"description": "Production environment"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
@@ -346,15 +443,23 @@ La UI de los docs interactuará con el server que selecciones.
|
||||
|
||||
///
|
||||
|
||||
### Desactivar el server automático de `root_path`
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
La propiedad `servers` en la especificación de OpenAPI es opcional.
|
||||
|
||||
Si no especificas el parámetro `servers` y `root_path` es igual a `/`, la propiedad `servers` en el esquema de OpenAPI generado se omitirá por completo por defecto, lo cual es equivalente a un único server con un valor `url` de `/`.
|
||||
|
||||
///
|
||||
|
||||
### Desactivar el server automático de `root_path` { #disable-automatic-server-from-root-path }
|
||||
|
||||
Si no quieres que **FastAPI** incluya un server automático usando el `root_path`, puedes usar el parámetro `root_path_in_servers=False`:
|
||||
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004.py hl[9] *}
|
||||
{* ../../docs_src/behind_a_proxy/tutorial004_py39.py hl[9] *}
|
||||
|
||||
y entonces no lo incluirá en el esquema de OpenAPI.
|
||||
|
||||
## Montando una sub-aplicación
|
||||
## Montando una sub-aplicación { #mounting-a-sub-application }
|
||||
|
||||
Si necesitas montar una sub-aplicación (como se describe en [Aplicaciones secundarias - Monturas](sub-applications.md){.internal-link target=_blank}) mientras usas un proxy con `root_path`, puedes hacerlo normalmente, como esperarías.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Response Personalizado - HTML, Stream, Archivo, otros
|
||||
# Response Personalizado - HTML, Stream, Archivo, otros { #custom-response-html-stream-file-others }
|
||||
|
||||
Por defecto, **FastAPI** devolverá los responses usando `JSONResponse`.
|
||||
|
||||
@@ -18,7 +18,7 @@ Si usas una clase de response sin media type, FastAPI esperará que tu response
|
||||
|
||||
///
|
||||
|
||||
## Usa `ORJSONResponse`
|
||||
## Usa `ORJSONResponse` { #use-orjsonresponse }
|
||||
|
||||
Por ejemplo, si estás exprimendo el rendimiento, puedes instalar y usar <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a> y establecer el response como `ORJSONResponse`.
|
||||
|
||||
@@ -30,7 +30,7 @@ Esto se debe a que, por defecto, FastAPI inspeccionará cada elemento dentro y s
|
||||
|
||||
Pero si estás seguro de que el contenido que estás devolviendo es **serializable con JSON**, puedes pasarlo directamente a la clase de response y evitar la sobrecarga extra que FastAPI tendría al pasar tu contenido de retorno a través de `jsonable_encoder` antes de pasarlo a la clase de response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001b.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001b_py39.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -48,14 +48,14 @@ El `ORJSONResponse` solo está disponible en FastAPI, no en Starlette.
|
||||
|
||||
///
|
||||
|
||||
## Response HTML
|
||||
## Response HTML { #html-response }
|
||||
|
||||
Para devolver un response con HTML directamente desde **FastAPI**, usa `HTMLResponse`.
|
||||
|
||||
* Importa `HTMLResponse`.
|
||||
* Pasa `HTMLResponse` como parámetro `response_class` de tu *path operation decorator*.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial002.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial002_py39.py hl[2,7] *}
|
||||
|
||||
/// info | Información
|
||||
|
||||
@@ -67,13 +67,13 @@ Y se documentará así en OpenAPI.
|
||||
|
||||
///
|
||||
|
||||
### Devuelve una `Response`
|
||||
### Devuelve una `Response` { #return-a-response }
|
||||
|
||||
Como se ve en [Devolver una Response directamente](response-directly.md){.internal-link target=_blank}, también puedes sobrescribir el response directamente en tu *path operation*, devolviéndolo.
|
||||
|
||||
El mismo ejemplo de arriba, devolviendo una `HTMLResponse`, podría verse así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial003.py hl[2,7,19] *}
|
||||
{* ../../docs_src/custom_response/tutorial003_py39.py hl[2,7,19] *}
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -87,27 +87,27 @@ Por supuesto, el `Content-Type` header real, el código de estado, etc., provend
|
||||
|
||||
///
|
||||
|
||||
### Documenta en OpenAPI y sobrescribe `Response`
|
||||
### Documenta en OpenAPI y sobrescribe `Response` { #document-in-openapi-and-override-response }
|
||||
|
||||
Si quieres sobrescribir el response desde dentro de la función pero al mismo tiempo documentar el "media type" en OpenAPI, puedes usar el parámetro `response_class` Y devolver un objeto `Response`.
|
||||
|
||||
El `response_class` solo se usará para documentar el OpenAPI *path operation*, pero tu `Response` se usará tal cual.
|
||||
|
||||
#### Devuelve un `HTMLResponse` directamente
|
||||
#### Devuelve un `HTMLResponse` directamente { #return-an-htmlresponse-directly }
|
||||
|
||||
Por ejemplo, podría ser algo así:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial004.py hl[7,21,23] *}
|
||||
{* ../../docs_src/custom_response/tutorial004_py39.py hl[7,21,23] *}
|
||||
|
||||
En este ejemplo, la función `generate_html_response()` ya genera y devuelve una `Response` en lugar de devolver el HTML en un `str`.
|
||||
|
||||
Al devolver el resultado de llamar a `generate_html_response()`, ya estás devolviendo una `Response` que sobrescribirá el comportamiento predeterminado de **FastAPI**.
|
||||
Al devolver el resultado de llamar a `generate_html_response()`, ya estás devolviendo una `Response` que sobrescribirá el comportamiento por defecto de **FastAPI**.
|
||||
|
||||
Pero como pasaste `HTMLResponse` en el `response_class` también, **FastAPI** sabrá cómo documentarlo en OpenAPI y la documentación interactiva como HTML con `text/html`:
|
||||
|
||||
<img src="/img/tutorial/custom-response/image01.png">
|
||||
|
||||
## Responses disponibles
|
||||
## Responses disponibles { #available-responses }
|
||||
|
||||
Aquí hay algunos de los responses disponibles.
|
||||
|
||||
@@ -121,7 +121,7 @@ También podrías usar `from starlette.responses import HTMLResponse`.
|
||||
|
||||
///
|
||||
|
||||
### `Response`
|
||||
### `Response` { #response }
|
||||
|
||||
La clase principal `Response`, todos los otros responses heredan de ella.
|
||||
|
||||
@@ -136,25 +136,25 @@ Acepta los siguientes parámetros:
|
||||
|
||||
FastAPI (de hecho Starlette) incluirá automáticamente un header Content-Length. También incluirá un header Content-Type, basado en el `media_type` y añadiendo un conjunto de caracteres para tipos de texto.
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
|
||||
### `HTMLResponse`
|
||||
### `HTMLResponse` { #htmlresponse }
|
||||
|
||||
Toma algún texto o bytes y devuelve un response HTML, como leíste arriba.
|
||||
|
||||
### `PlainTextResponse`
|
||||
### `PlainTextResponse` { #plaintextresponse }
|
||||
|
||||
Toma algún texto o bytes y devuelve un response de texto plano.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial005.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial005_py39.py hl[2,7,9] *}
|
||||
|
||||
### `JSONResponse`
|
||||
### `JSONResponse` { #jsonresponse }
|
||||
|
||||
Toma algunos datos y devuelve un response codificado como `application/json`.
|
||||
|
||||
Este es el response predeterminado usado en **FastAPI**, como leíste arriba.
|
||||
Este es el response usado por defecto en **FastAPI**, como leíste arriba.
|
||||
|
||||
### `ORJSONResponse`
|
||||
### `ORJSONResponse` { #orjsonresponse }
|
||||
|
||||
Un response JSON rápido alternativo usando <a href="https://github.com/ijl/orjson" class="external-link" target="_blank">`orjson`</a>, como leíste arriba.
|
||||
|
||||
@@ -164,7 +164,7 @@ Esto requiere instalar `orjson`, por ejemplo, con `pip install orjson`.
|
||||
|
||||
///
|
||||
|
||||
### `UJSONResponse`
|
||||
### `UJSONResponse` { #ujsonresponse }
|
||||
|
||||
Un response JSON alternativo usando <a href="https://github.com/ultrajson/ultrajson" class="external-link" target="_blank">`ujson`</a>.
|
||||
|
||||
@@ -180,7 +180,7 @@ Esto requiere instalar `ujson`, por ejemplo, con `pip install ujson`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial001.py hl[2,7] *}
|
||||
{* ../../docs_src/custom_response/tutorial001_py39.py hl[2,7] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -188,45 +188,45 @@ Es posible que `ORJSONResponse` sea una alternativa más rápida.
|
||||
|
||||
///
|
||||
|
||||
### `RedirectResponse`
|
||||
### `RedirectResponse` { #redirectresponse }
|
||||
|
||||
Devuelve una redirección HTTP. Usa un código de estado 307 (Redirección Temporal) por defecto.
|
||||
|
||||
Puedes devolver un `RedirectResponse` directamente:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006.py hl[2,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006_py39.py hl[2,9] *}
|
||||
|
||||
---
|
||||
|
||||
O puedes usarlo en el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006b.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006b_py39.py hl[2,7,9] *}
|
||||
|
||||
Si haces eso, entonces puedes devolver la URL directamente desde tu *path operation function*.
|
||||
|
||||
En este caso, el `status_code` utilizado será el predeterminado para `RedirectResponse`, que es `307`.
|
||||
En este caso, el `status_code` utilizado será el por defecto para `RedirectResponse`, que es `307`.
|
||||
|
||||
---
|
||||
|
||||
También puedes usar el parámetro `status_code` combinado con el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial006c.py hl[2,7,9] *}
|
||||
{* ../../docs_src/custom_response/tutorial006c_py39.py hl[2,7,9] *}
|
||||
|
||||
### `StreamingResponse`
|
||||
### `StreamingResponse` { #streamingresponse }
|
||||
|
||||
Toma un generador `async` o un generador/iterador normal y transmite el cuerpo del response.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial007.py hl[2,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial007_py39.py hl[2,14] *}
|
||||
|
||||
#### Usando `StreamingResponse` con objetos similares a archivos
|
||||
#### Usando `StreamingResponse` con objetos similares a archivos { #using-streamingresponse-with-file-like-objects }
|
||||
|
||||
Si tienes un objeto similar a un archivo (por ejemplo, el objeto devuelto por `open()`), puedes crear una función generadora para iterar sobre ese objeto similar a un archivo.
|
||||
Si tienes un <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">objeto similar a un archivo</a> (por ejemplo, el objeto devuelto por `open()`), puedes crear una función generadora para iterar sobre ese objeto similar a un archivo.
|
||||
|
||||
De esa manera, no tienes que leerlo todo primero en memoria, y puedes pasar esa función generadora al `StreamingResponse`, y devolverlo.
|
||||
|
||||
Esto incluye muchos paquetes para interactuar con almacenamiento en la nube, procesamiento de video y otros.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial008.py hl[2,10:12,14] *}
|
||||
{* ../../docs_src/custom_response/tutorial008_py39.py hl[2,10:12,14] *}
|
||||
|
||||
1. Esta es la función generadora. Es una "función generadora" porque contiene declaraciones `yield` dentro.
|
||||
2. Al usar un bloque `with`, nos aseguramos de que el objeto similar a un archivo se cierre después de que la función generadora termine. Así, después de que termina de enviar el response.
|
||||
@@ -242,7 +242,7 @@ Nota que aquí como estamos usando `open()` estándar que no admite `async` y `a
|
||||
|
||||
///
|
||||
|
||||
### `FileResponse`
|
||||
### `FileResponse` { #fileresponse }
|
||||
|
||||
Transmite un archivo asincrónicamente como response.
|
||||
|
||||
@@ -255,15 +255,15 @@ Toma un conjunto diferente de argumentos para crear un instance que los otros ti
|
||||
|
||||
Los responses de archivos incluirán los headers apropiados `Content-Length`, `Last-Modified` y `ETag`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009.py hl[2,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009_py39.py hl[2,10] *}
|
||||
|
||||
También puedes usar el parámetro `response_class`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009b.py hl[2,8,10] *}
|
||||
{* ../../docs_src/custom_response/tutorial009b_py39.py hl[2,8,10] *}
|
||||
|
||||
En este caso, puedes devolver la path del archivo directamente desde tu *path operation* function.
|
||||
|
||||
## Clase de response personalizada
|
||||
## Clase de response personalizada { #custom-response-class }
|
||||
|
||||
Puedes crear tu propia clase de response personalizada, heredando de `Response` y usándola.
|
||||
|
||||
@@ -273,7 +273,7 @@ Digamos que quieres que devuelva JSON con sangría y formato, por lo que quieres
|
||||
|
||||
Podrías crear un `CustomORJSONResponse`. Lo principal que tienes que hacer es crear un método `Response.render(content)` que devuelva el contenido como `bytes`:
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial009c.py hl[9:14,17] *}
|
||||
{* ../../docs_src/custom_response/tutorial009c_py39.py hl[9:14,17] *}
|
||||
|
||||
Ahora en lugar de devolver:
|
||||
|
||||
@@ -291,7 +291,7 @@ Ahora en lugar de devolver:
|
||||
|
||||
Por supuesto, probablemente encontrarás formas mucho mejores de aprovechar esto que formatear JSON. 😉
|
||||
|
||||
## Clase de response predeterminada
|
||||
## Clase de response por defecto { #default-response-class }
|
||||
|
||||
Al crear una instance de la clase **FastAPI** o un `APIRouter`, puedes especificar qué clase de response usar por defecto.
|
||||
|
||||
@@ -299,7 +299,7 @@ El parámetro que define esto es `default_response_class`.
|
||||
|
||||
En el ejemplo a continuación, **FastAPI** usará `ORJSONResponse` por defecto, en todas las *path operations*, en lugar de `JSONResponse`.
|
||||
|
||||
{* ../../docs_src/custom_response/tutorial010.py hl[2,4] *}
|
||||
{* ../../docs_src/custom_response/tutorial010_py39.py hl[2,4] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -307,6 +307,6 @@ Todavía puedes sobrescribir `response_class` en *path operations* como antes.
|
||||
|
||||
///
|
||||
|
||||
## Documentación adicional
|
||||
## Documentación adicional { #additional-documentation }
|
||||
|
||||
También puedes declarar el media type y muchos otros detalles en OpenAPI usando `responses`: [Responses Adicionales en OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Usando Dataclasses
|
||||
# Usando Dataclasses { #using-dataclasses }
|
||||
|
||||
FastAPI está construido sobre **Pydantic**, y te he estado mostrando cómo usar modelos de Pydantic para declarar requests y responses.
|
||||
|
||||
Pero FastAPI también soporta el uso de <a href="https://docs.python.org/3/library/dataclasses.html" class="external-link" target="_blank">`dataclasses`</a> de la misma manera:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial001.py hl[1,7:12,19:20] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *}
|
||||
|
||||
Esto sigue siendo soportado gracias a **Pydantic**, ya que tiene <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel" class="external-link" target="_blank">soporte interno para `dataclasses`</a>.
|
||||
|
||||
@@ -28,11 +28,11 @@ Pero si tienes un montón de dataclasses por ahí, este es un buen truco para us
|
||||
|
||||
///
|
||||
|
||||
## Dataclasses en `response_model`
|
||||
## Dataclasses en `response_model` { #dataclasses-in-response-model }
|
||||
|
||||
También puedes usar `dataclasses` en el parámetro `response_model`:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial002.py hl[1,7:13,19] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *}
|
||||
|
||||
El dataclass será automáticamente convertido a un dataclass de Pydantic.
|
||||
|
||||
@@ -40,7 +40,7 @@ De esta manera, su esquema aparecerá en la interfaz de usuario de la documentac
|
||||
|
||||
<img src="/img/tutorial/dataclasses/image01.png">
|
||||
|
||||
## Dataclasses en Estructuras de Datos Anidadas
|
||||
## Dataclasses en Estructuras de Datos Anidadas { #dataclasses-in-nested-data-structures }
|
||||
|
||||
También puedes combinar `dataclasses` con otras anotaciones de tipos para crear estructuras de datos anidadas.
|
||||
|
||||
@@ -48,7 +48,7 @@ En algunos casos, todavía podrías tener que usar la versión de `dataclasses`
|
||||
|
||||
En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `pydantic.dataclasses`, que es un reemplazo directo:
|
||||
|
||||
{* ../../docs_src/dataclasses/tutorial003.py hl[1,5,8:11,14:17,23:25,28] *}
|
||||
{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *}
|
||||
|
||||
1. Todavía importamos `field` de los `dataclasses` estándar.
|
||||
|
||||
@@ -64,7 +64,7 @@ En ese caso, simplemente puedes intercambiar los `dataclasses` estándar con `py
|
||||
|
||||
6. Aquí estamos regresando un diccionario que contiene `items`, que es una lista de dataclasses.
|
||||
|
||||
FastAPI todavía es capaz de <abbr title="converting the data to a format that can be transmitted">serializar</abbr> los datos a JSON.
|
||||
FastAPI todavía es capaz de <abbr title="convertir los datos a un formato que pueda transmitirse">serializar</abbr> los datos a JSON.
|
||||
|
||||
7. Aquí el `response_model` está usando una anotación de tipo de una lista de dataclasses `Author`.
|
||||
|
||||
@@ -84,12 +84,12 @@ Puedes combinar `dataclasses` con otras anotaciones de tipos en muchas combinaci
|
||||
|
||||
Revisa las anotaciones en el código arriba para ver más detalles específicos.
|
||||
|
||||
## Aprende Más
|
||||
## Aprende Más { #learn-more }
|
||||
|
||||
También puedes combinar `dataclasses` con otros modelos de Pydantic, heredar de ellos, incluirlos en tus propios modelos, etc.
|
||||
|
||||
Para saber más, revisa la <a href="https://docs.pydantic.dev/latest/concepts/dataclasses/" class="external-link" target="_blank">documentación de Pydantic sobre dataclasses</a>.
|
||||
|
||||
## Versión
|
||||
## Versión { #version }
|
||||
|
||||
Esto está disponible desde la versión `0.67.0` de FastAPI. 🔖
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Eventos de Lifespan
|
||||
# Eventos de Lifespan { #lifespan-events }
|
||||
|
||||
Puedes definir lógica (código) que debería ser ejecutada antes de que la aplicación **inicie**. Esto significa que este código será ejecutado **una vez**, **antes** de que la aplicación **comience a recibir requests**.
|
||||
|
||||
@@ -8,7 +8,7 @@ Debido a que este código se ejecuta antes de que la aplicación **comience** a
|
||||
|
||||
Esto puede ser muy útil para configurar **recursos** que necesitas usar para toda la app, y que son **compartidos** entre requests, y/o que necesitas **limpiar** después. Por ejemplo, un pool de conexiones a una base de datos, o cargando un modelo de machine learning compartido.
|
||||
|
||||
## Caso de Uso
|
||||
## Caso de Uso { #use-case }
|
||||
|
||||
Empecemos con un ejemplo de **caso de uso** y luego veamos cómo resolverlo con esto.
|
||||
|
||||
@@ -22,7 +22,7 @@ Podrías cargarlo en el nivel superior del módulo/archivo, pero eso también si
|
||||
|
||||
Eso es lo que resolveremos, vamos a cargar el modelo antes de que los requests sean manejados, pero solo justo antes de que la aplicación comience a recibir requests, no mientras el código se está cargando.
|
||||
|
||||
## Lifespan
|
||||
## Lifespan { #lifespan }
|
||||
|
||||
Puedes definir esta lógica de *startup* y *shutdown* usando el parámetro `lifespan` de la app de `FastAPI`, y un "context manager" (te mostraré lo que es en un momento).
|
||||
|
||||
@@ -30,7 +30,7 @@ Comencemos con un ejemplo y luego veámoslo en detalle.
|
||||
|
||||
Creamos una función asíncrona `lifespan()` con `yield` así:
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[16,19] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[16,19] *}
|
||||
|
||||
Aquí estamos simulando la operación costosa de *startup* de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes del `yield`. Este código será ejecutado **antes** de que la aplicación **comience a tomar requests**, durante el *startup*.
|
||||
|
||||
@@ -44,25 +44,25 @@ Quizás necesites iniciar una nueva versión, o simplemente te cansaste de ejecu
|
||||
|
||||
///
|
||||
|
||||
### Función de Lifespan
|
||||
### Función de Lifespan { #lifespan-function }
|
||||
|
||||
Lo primero que hay que notar es que estamos definiendo una función asíncrona con `yield`. Esto es muy similar a las Dependencias con `yield`.
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[14:19] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[14:19] *}
|
||||
|
||||
La primera parte de la función, antes del `yield`, será ejecutada **antes** de que la aplicación comience.
|
||||
|
||||
Y la parte después del `yield` será ejecutada **después** de que la aplicación haya terminado.
|
||||
|
||||
### Async Context Manager
|
||||
### Async Context Manager { #async-context-manager }
|
||||
|
||||
Si revisas, la función está decorada con un `@asynccontextmanager`.
|
||||
|
||||
Eso convierte a la función en algo llamado un "**async context manager**".
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[1,13] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[1,13] *}
|
||||
|
||||
Un **context manager** en Python es algo que puedes usar en una declaración `with`, por ejemplo, `open()` puede ser usado como un context manager:
|
||||
Un **context manager** en Python es algo que puedes usar en un statement `with`, por ejemplo, `open()` puede ser usado como un context manager:
|
||||
|
||||
```Python
|
||||
with open("file.txt") as file:
|
||||
@@ -82,9 +82,9 @@ En nuestro ejemplo de código arriba, no lo usamos directamente, pero se lo pasa
|
||||
|
||||
El parámetro `lifespan` de la app de `FastAPI` toma un **async context manager**, por lo que podemos pasar nuestro nuevo `lifespan` async context manager a él.
|
||||
|
||||
{* ../../docs_src/events/tutorial003.py hl[22] *}
|
||||
{* ../../docs_src/events/tutorial003_py39.py hl[22] *}
|
||||
|
||||
## Eventos Alternativos (obsoleto)
|
||||
## Eventos Alternativos (obsoleto) { #alternative-events-deprecated }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -100,11 +100,11 @@ Puedes definir manejadores de eventos (funciones) que necesitan ser ejecutadas a
|
||||
|
||||
Estas funciones pueden ser declaradas con `async def` o `def` normal.
|
||||
|
||||
### Evento `startup`
|
||||
### Evento `startup` { #startup-event }
|
||||
|
||||
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento `"startup"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial001.py hl[8] *}
|
||||
{* ../../docs_src/events/tutorial001_py39.py hl[8] *}
|
||||
|
||||
En este caso, la función manejadora del evento `startup` inicializará los ítems de la "base de datos" (solo un `dict`) con algunos valores.
|
||||
|
||||
@@ -112,11 +112,11 @@ Puedes añadir más de un manejador de eventos.
|
||||
|
||||
Y tu aplicación no comenzará a recibir requests hasta que todos los manejadores de eventos `startup` hayan completado.
|
||||
|
||||
### Evento `shutdown`
|
||||
### Evento `shutdown` { #shutdown-event }
|
||||
|
||||
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento `"shutdown"`:
|
||||
|
||||
{* ../../docs_src/events/tutorial002.py hl[6] *}
|
||||
{* ../../docs_src/events/tutorial002_py39.py hl[6] *}
|
||||
|
||||
Aquí, la función manejadora del evento `shutdown` escribirá una línea de texto `"Application shutdown"` a un archivo `log.txt`.
|
||||
|
||||
@@ -138,7 +138,7 @@ Por eso, declaramos la función manejadora del evento con `def` estándar en vez
|
||||
|
||||
///
|
||||
|
||||
### `startup` y `shutdown` juntos
|
||||
### `startup` y `shutdown` juntos { #startup-and-shutdown-together }
|
||||
|
||||
Hay una gran posibilidad de que la lógica para tu *startup* y *shutdown* esté conectada, podrías querer iniciar algo y luego finalizarlo, adquirir un recurso y luego liberarlo, etc.
|
||||
|
||||
@@ -146,7 +146,7 @@ Hacer eso en funciones separadas que no comparten lógica o variables juntas es
|
||||
|
||||
Debido a eso, ahora se recomienda en su lugar usar el `lifespan` como se explicó arriba.
|
||||
|
||||
## Detalles Técnicos
|
||||
## Detalles Técnicos { #technical-details }
|
||||
|
||||
Solo un detalle técnico para los nerds curiosos. 🤓
|
||||
|
||||
@@ -160,6 +160,6 @@ Incluyendo cómo manejar el estado de lifespan que puede ser usado en otras áre
|
||||
|
||||
///
|
||||
|
||||
## Sub Aplicaciones
|
||||
## Sub Aplicaciones { #sub-applications }
|
||||
|
||||
🚨 Ten en cuenta que estos eventos de lifespan (startup y shutdown) solo serán ejecutados para la aplicación principal, no para [Sub Aplicaciones - Mounts](sub-applications.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -1,115 +1,76 @@
|
||||
# Genera Clientes
|
||||
# Generando SDKs { #generating-sdks }
|
||||
|
||||
Como **FastAPI** está basado en la especificación OpenAPI, obtienes compatibilidad automática con muchas herramientas, incluyendo la documentación automática de la API (proporcionada por Swagger UI).
|
||||
Como **FastAPI** está basado en la especificación **OpenAPI**, sus APIs se pueden describir en un formato estándar que muchas herramientas entienden.
|
||||
|
||||
Una ventaja particular que no es necesariamente obvia es que puedes **generar clientes** (a veces llamados <abbr title="Software Development Kits">**SDKs**</abbr> ) para tu API, para muchos **lenguajes de programación** diferentes.
|
||||
Esto facilita generar **documentación** actualizada, paquetes de cliente (<abbr title="Software Development Kits – Kits de Desarrollo de Software">**SDKs**</abbr>) en múltiples lenguajes y **escribir pruebas** o **flujos de automatización** que se mantengan sincronizados con tu código.
|
||||
|
||||
## Generadores de Clientes OpenAPI
|
||||
En esta guía, aprenderás a generar un **SDK de TypeScript** para tu backend con FastAPI.
|
||||
|
||||
Hay muchas herramientas para generar clientes desde **OpenAPI**.
|
||||
## Generadores de SDKs de código abierto { #open-source-sdk-generators }
|
||||
|
||||
Una herramienta común es <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
|
||||
Una opción versátil es el <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>, que soporta **muchos lenguajes de programación** y puede generar SDKs a partir de tu especificación OpenAPI.
|
||||
|
||||
Si estás construyendo un **frontend**, una alternativa muy interesante es <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>.
|
||||
Para **clientes de TypeScript**, <a href="https://heyapi.dev/" class="external-link" target="_blank">Hey API</a> es una solución diseñada específicamente, que ofrece una experiencia optimizada para el ecosistema de TypeScript.
|
||||
|
||||
## Generadores de Clientes y SDKs - Sponsor
|
||||
Puedes descubrir más generadores de SDK en <a href="https://openapi.tools/#sdk" class="external-link" target="_blank">OpenAPI.Tools</a>.
|
||||
|
||||
También hay algunos generadores de Clientes y SDKs **respaldados por empresas** basados en OpenAPI (FastAPI), en algunos casos pueden ofrecerte **funcionalidades adicionales** además de SDKs/clientes generados de alta calidad.
|
||||
/// tip | Consejo
|
||||
|
||||
Algunos de ellos también ✨ [**sponsorean FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, esto asegura el **desarrollo** continuo y saludable de FastAPI y su **ecosistema**.
|
||||
FastAPI genera automáticamente especificaciones **OpenAPI 3.1**, así que cualquier herramienta que uses debe soportar esta versión.
|
||||
|
||||
Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que no solo quieren proporcionarte un **buen servicio** sino también asegurarse de que tengas un **buen y saludable framework**, FastAPI. 🙇
|
||||
///
|
||||
|
||||
## Generadores de SDKs de sponsors de FastAPI { #sdk-generators-from-fastapi-sponsors }
|
||||
|
||||
Esta sección destaca soluciones **respaldadas por empresas** y **venture-backed** de compañías que sponsorean FastAPI. Estos productos ofrecen **funcionalidades adicionales** e **integraciones** además de SDKs generados de alta calidad.
|
||||
|
||||
Al ✨ [**sponsorear FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, estas compañías ayudan a asegurar que el framework y su **ecosistema** se mantengan saludables y **sustentables**.
|
||||
|
||||
Su sponsorship también demuestra un fuerte compromiso con la **comunidad** de FastAPI (tú), mostrando que no solo les importa ofrecer un **gran servicio**, sino también apoyar un **framework robusto y próspero**, FastAPI. 🙇
|
||||
|
||||
Por ejemplo, podrías querer probar:
|
||||
|
||||
* <a href="https://speakeasy.com/editor?utm_source=fastapi+repo&utm_medium=github+sponsorship" class="external-link" target="_blank">Speakeasy</a>
|
||||
* <a href="https://www.stainlessapi.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi/?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
* <a href="https://www.stainless.com/?utm_source=fastapi&utm_medium=referral" class="external-link" target="_blank">Stainless</a>
|
||||
* <a href="https://developers.liblab.com/tutorials/sdk-for-fastapi?utm_source=fastapi" class="external-link" target="_blank">liblab</a>
|
||||
|
||||
También hay varias otras empresas que ofrecen servicios similares que puedes buscar y encontrar en línea. 🤓
|
||||
Algunas de estas soluciones también pueden ser open source u ofrecer niveles gratuitos, así que puedes probarlas sin un compromiso financiero. Hay otros generadores de SDK comerciales disponibles y se pueden encontrar en línea. 🤓
|
||||
|
||||
## Genera un Cliente Frontend en TypeScript
|
||||
## Crea un SDK de TypeScript { #create-a-typescript-sdk }
|
||||
|
||||
Empecemos con una aplicación simple de FastAPI:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial001_py39.py hl[7:9,12:13,16:17,21] *}
|
||||
|
||||
Nota que las *path operations* definen los modelos que usan para el payload de la petición y el payload del response, usando los modelos `Item` y `ResponseMessage`.
|
||||
Nota que las *path operations* definen los modelos que usan para el payload del request y el payload del response, usando los modelos `Item` y `ResponseMessage`.
|
||||
|
||||
### Documentación de la API
|
||||
### Documentación de la API { #api-docs }
|
||||
|
||||
Si vas a la documentación de la API, verás que tiene los **esquemas** para los datos que se enviarán en las peticiones y se recibirán en los responses:
|
||||
Si vas a `/docs`, verás que tiene los **esquemas** para los datos a enviar en requests y recibir en responses:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image01.png">
|
||||
|
||||
Puedes ver esos esquemas porque fueron declarados con los modelos en la aplicación.
|
||||
Puedes ver esos esquemas porque fueron declarados con los modelos en la app.
|
||||
|
||||
Esa información está disponible en el **JSON Schema** de OpenAPI de la aplicación, y luego se muestra en la documentación de la API (por Swagger UI).
|
||||
Esa información está disponible en el **OpenAPI schema** de la app, y luego se muestra en la documentación de la API.
|
||||
|
||||
Y esa misma información de los modelos que está incluida en OpenAPI es lo que puede usarse para **generar el código del cliente**.
|
||||
|
||||
### Genera un Cliente en TypeScript
|
||||
### Hey API { #hey-api }
|
||||
|
||||
Ahora que tenemos la aplicación con los modelos, podemos generar el código del cliente para el frontend.
|
||||
Una vez que tenemos una app de FastAPI con los modelos, podemos usar Hey API para generar un cliente de TypeScript. La forma más rápida de hacerlo es con npx.
|
||||
|
||||
#### Instalar `openapi-ts`
|
||||
|
||||
Puedes instalar `openapi-ts` en tu código de frontend con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client
|
||||
```
|
||||
|
||||
</div>
|
||||
Esto generará un SDK de TypeScript en `./src/client`.
|
||||
|
||||
#### Generar el Código del Cliente
|
||||
Puedes aprender cómo <a href="https://heyapi.dev/openapi-ts/get-started" class="external-link" target="_blank">instalar `@hey-api/openapi-ts`</a> y leer sobre el <a href="https://heyapi.dev/openapi-ts/output" class="external-link" target="_blank">output generado</a> en su sitio web.
|
||||
|
||||
Para generar el código del cliente puedes usar la aplicación de línea de comandos `openapi-ts` que ahora estaría instalada.
|
||||
### Usar el SDK { #using-the-sdk }
|
||||
|
||||
Como está instalada en el proyecto local, probablemente no podrías llamar a ese comando directamente, pero podrías ponerlo en tu archivo `package.json`.
|
||||
|
||||
Podría verse como esto:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Después de tener ese script de NPM `generate-client` allí, puedes ejecutarlo con:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Ese comando generará código en `./src/client` y usará `axios` (el paquete HTTP de frontend) internamente.
|
||||
|
||||
### Prueba el Código del Cliente
|
||||
|
||||
Ahora puedes importar y usar el código del cliente, podría verse así, nota que tienes autocompletado para los métodos:
|
||||
Ahora puedes importar y usar el código del cliente. Podría verse así, nota que tienes autocompletado para los métodos:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image02.png">
|
||||
|
||||
@@ -131,17 +92,17 @@ El objeto de response también tendrá autocompletado:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image05.png">
|
||||
|
||||
## App de FastAPI con Tags
|
||||
## App de FastAPI con tags { #fastapi-app-with-tags }
|
||||
|
||||
En muchos casos tu aplicación de FastAPI será más grande, y probablemente usarás tags para separar diferentes grupos de *path operations*.
|
||||
En muchos casos tu app de FastAPI será más grande, y probablemente usarás tags para separar diferentes grupos de *path operations*.
|
||||
|
||||
Por ejemplo, podrías tener una sección para **items** y otra sección para **usuarios**, y podrían estar separadas por tags:
|
||||
Por ejemplo, podrías tener una sección para **items** y otra sección para **users**, y podrían estar separadas por tags:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial002_py39.py hl[21,26,34] *}
|
||||
|
||||
### Genera un Cliente TypeScript con Tags
|
||||
### Genera un Cliente TypeScript con tags { #generate-a-typescript-client-with-tags }
|
||||
|
||||
Si generas un cliente para una aplicación de FastAPI usando tags, normalmente también separará el código del cliente basándose en los tags.
|
||||
Si generas un cliente para una app de FastAPI usando tags, normalmente también separará el código del cliente basándose en los tags.
|
||||
|
||||
De esta manera podrás tener las cosas ordenadas y agrupadas correctamente para el código del cliente:
|
||||
|
||||
@@ -152,7 +113,7 @@ En este caso tienes:
|
||||
* `ItemsService`
|
||||
* `UsersService`
|
||||
|
||||
### Nombres de los Métodos del Cliente
|
||||
### Nombres de los métodos del cliente { #client-method-names }
|
||||
|
||||
Ahora mismo los nombres de los métodos generados como `createItemItemsPost` no se ven muy limpios:
|
||||
|
||||
@@ -166,15 +127,15 @@ OpenAPI requiere que cada operation ID sea único a través de todas las *path o
|
||||
|
||||
Pero te mostraré cómo mejorar eso a continuación. 🤓
|
||||
|
||||
## Operation IDs Personalizados y Mejores Nombres de Métodos
|
||||
## Operation IDs personalizados y mejores nombres de métodos { #custom-operation-ids-and-better-method-names }
|
||||
|
||||
Puedes **modificar** la forma en que estos operation IDs son **generados** para hacerlos más simples y tener **nombres de métodos más simples** en los clientes.
|
||||
|
||||
En este caso tendrás que asegurarte de que cada operation ID sea **único** de alguna otra manera.
|
||||
|
||||
Por ejemplo, podrías asegurarte de que cada *path operation* tenga un tag, y luego generar el operation ID basado en el **tag** y el nombre de la *path operation* **name** (el nombre de la función).
|
||||
Por ejemplo, podrías asegurarte de que cada *path operation* tenga un tag, y luego generar el operation ID basado en el **tag** y el **name** de la *path operation* (el nombre de la función).
|
||||
|
||||
### Función Personalizada para Generar ID Único
|
||||
### Función personalizada para generar ID único { #custom-generate-unique-id-function }
|
||||
|
||||
FastAPI usa un **ID único** para cada *path operation*, se usa para el **operation ID** y también para los nombres de cualquier modelo personalizado necesario, para requests o responses.
|
||||
|
||||
@@ -186,15 +147,15 @@ Puedes entonces pasar esa función personalizada a **FastAPI** como el parámetr
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial003_py39.py hl[6:7,10] *}
|
||||
|
||||
### Generar un Cliente TypeScript con Operation IDs Personalizados
|
||||
### Genera un Cliente TypeScript con operation IDs personalizados { #generate-a-typescript-client-with-custom-operation-ids }
|
||||
|
||||
Ahora si generas el cliente de nuevo, verás que tiene los nombres de métodos mejorados:
|
||||
Ahora, si generas el cliente de nuevo, verás que tiene los nombres de métodos mejorados:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image07.png">
|
||||
|
||||
Como ves, los nombres de métodos ahora tienen el tag y luego el nombre de la función, ahora no incluyen información del path de la URL y la operación HTTP.
|
||||
|
||||
### Preprocesa la Especificación OpenAPI para el Generador de Clientes
|
||||
### Preprocesa la especificación OpenAPI para el generador de clientes { #preprocess-the-openapi-specification-for-the-client-generator }
|
||||
|
||||
El código generado aún tiene algo de **información duplicada**.
|
||||
|
||||
@@ -206,7 +167,7 @@ Pero para el cliente generado podríamos **modificar** los operation IDs de Open
|
||||
|
||||
Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podríamos **remover ese tag prefijado** con un script como este:
|
||||
|
||||
{* ../../docs_src/generate_clients/tutorial004.py *}
|
||||
{* ../../docs_src/generate_clients/tutorial004_py39.py *}
|
||||
|
||||
//// tab | Node.js
|
||||
|
||||
@@ -218,44 +179,30 @@ Podríamos descargar el JSON de OpenAPI a un archivo `openapi.json` y luego podr
|
||||
|
||||
Con eso, los operation IDs serían renombrados de cosas como `items-get_items` a solo `get_items`, de esa manera el generador del cliente puede generar nombres de métodos más simples.
|
||||
|
||||
### Generar un Cliente TypeScript con el OpenAPI Preprocesado
|
||||
### Genera un Cliente TypeScript con el OpenAPI preprocesado { #generate-a-typescript-client-with-the-preprocessed-openapi }
|
||||
|
||||
Ahora como el resultado final está en un archivo `openapi.json`, modificarías el `package.json` para usar ese archivo local, por ejemplo:
|
||||
Como el resultado final ahora está en un archivo `openapi.json`, necesitas actualizar la ubicación de la entrada:
|
||||
|
||||
```JSON hl_lines="7"
|
||||
{
|
||||
"name": "frontend-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
```sh
|
||||
npx @hey-api/openapi-ts -i ./openapi.json -o src/client
|
||||
```
|
||||
|
||||
Después de generar el nuevo cliente, ahora tendrías nombres de métodos **limpios**, con todo el **autocompletado**, **errores en línea**, etc:
|
||||
|
||||
<img src="/img/tutorial/generate-clients/image08.png">
|
||||
|
||||
## Beneficios
|
||||
## Beneficios { #benefits }
|
||||
|
||||
Cuando usas los clientes generados automáticamente obtendrás **autocompletado** para:
|
||||
Cuando uses los clientes generados automáticamente obtendrás **autocompletado** para:
|
||||
|
||||
* Métodos.
|
||||
* Payloads de peticiones en el cuerpo, parámetros de query, etc.
|
||||
* Payloads de responses.
|
||||
* Payloads de request en el body, parámetros de query, etc.
|
||||
* Payloads de response.
|
||||
|
||||
También tendrás **errores en línea** para todo.
|
||||
|
||||
Y cada vez que actualices el código del backend, y **regeneres** el frontend, tendrás las nuevas *path operations* disponibles como métodos, las antiguas eliminadas, y cualquier otro cambio se reflejará en el código generado. 🤓
|
||||
|
||||
Esto también significa que si algo cambió será **reflejado** automáticamente en el código del cliente. Y si haces **build** del cliente, te dará error si tienes algún **desajuste** en los datos utilizados.
|
||||
Esto también significa que si algo cambió será **reflejado** automáticamente en el código del cliente. Y si haces **build** del cliente, dará error si tienes algún **desajuste** en los datos utilizados.
|
||||
|
||||
Así que, **detectarás muchos errores** muy temprano en el ciclo de desarrollo en lugar de tener que esperar a que los errores se muestren a tus usuarios finales en producción para luego intentar depurar dónde está el problema. ✨
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Guía avanzada del usuario
|
||||
# Guía avanzada del usuario { #advanced-user-guide }
|
||||
|
||||
## Funcionalidades adicionales
|
||||
## Funcionalidades adicionales { #additional-features }
|
||||
|
||||
El [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} principal debería ser suficiente para darte un recorrido por todas las funcionalidades principales de **FastAPI**.
|
||||
|
||||
@@ -14,23 +14,8 @@ Y es posible que para tu caso de uso, la solución esté en una de ellas.
|
||||
|
||||
///
|
||||
|
||||
## Lee primero el Tutorial
|
||||
## Lee primero el Tutorial { #read-the-tutorial-first }
|
||||
|
||||
Aún podrías usar la mayoría de las funcionalidades en **FastAPI** con el conocimiento del [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} principal.
|
||||
|
||||
Y las siguientes secciones asumen que ya lo leíste y que conoces esas ideas principales.
|
||||
|
||||
## Cursos externos
|
||||
|
||||
Aunque el [Tutorial - Guía del usuario](../tutorial/index.md){.internal-link target=_blank} y esta **Guía avanzada del usuario** están escritos como un tutorial guiado (como un libro) y deberían ser suficientes para que **aprendas FastAPI**, podrías querer complementarlo con cursos adicionales.
|
||||
|
||||
O podría ser que simplemente prefieras tomar otros cursos porque se adaptan mejor a tu estilo de aprendizaje.
|
||||
|
||||
Algunos proveedores de cursos ✨ [**sponsorean FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} ✨, esto asegura el desarrollo continuo y saludable de FastAPI y su **ecosistema**.
|
||||
|
||||
Y muestra su verdadero compromiso con FastAPI y su **comunidad** (tú), ya que no solo quieren brindarte una **buena experiencia de aprendizaje** sino que también quieren asegurarse de que tengas un **buen y saludable framework**, FastAPI. 🙇
|
||||
|
||||
Podrías querer probar sus cursos:
|
||||
|
||||
* <a href="https://training.talkpython.fm/fastapi-courses" class="external-link" target="_blank">Talk Python Training</a>
|
||||
* <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">Desarrollo guiado por pruebas</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Middleware Avanzado
|
||||
# Middleware Avanzado { #advanced-middleware }
|
||||
|
||||
En el tutorial principal leíste cómo agregar [Middleware Personalizado](../tutorial/middleware.md){.internal-link target=_blank} a tu aplicación.
|
||||
|
||||
@@ -6,9 +6,9 @@ Y luego también leíste cómo manejar [CORS con el `CORSMiddleware`](../tutoria
|
||||
|
||||
En esta sección veremos cómo usar otros middlewares.
|
||||
|
||||
## Agregando middlewares ASGI
|
||||
## Agregando middlewares ASGI { #adding-asgi-middlewares }
|
||||
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
Como **FastAPI** está basado en Starlette e implementa la especificación <abbr title="Asynchronous Server Gateway Interface – Interfaz de puerta de enlace de servidor asíncrona">ASGI</abbr>, puedes usar cualquier middleware ASGI.
|
||||
|
||||
Un middleware no tiene que estar hecho para FastAPI o Starlette para funcionar, siempre que siga la especificación ASGI.
|
||||
|
||||
@@ -39,7 +39,7 @@ app.add_middleware(UnicornMiddleware, some_config="rainbow")
|
||||
|
||||
`app.add_middleware()` recibe una clase de middleware como primer argumento y cualquier argumento adicional que se le quiera pasar al middleware.
|
||||
|
||||
## Middlewares integrados
|
||||
## Middlewares integrados { #integrated-middlewares }
|
||||
|
||||
**FastAPI** incluye varios middlewares para casos de uso común, veremos a continuación cómo usarlos.
|
||||
|
||||
@@ -51,40 +51,41 @@ Para los próximos ejemplos, también podrías usar `from starlette.middleware.s
|
||||
|
||||
///
|
||||
|
||||
## `HTTPSRedirectMiddleware`
|
||||
## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware }
|
||||
|
||||
Impone que todas las requests entrantes deben ser `https` o `wss`.
|
||||
|
||||
Cualquier request entrante a `http` o `ws` será redirigida al esquema seguro.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial001.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial001_py39.py hl[2,6] *}
|
||||
|
||||
## `TrustedHostMiddleware`
|
||||
## `TrustedHostMiddleware` { #trustedhostmiddleware }
|
||||
|
||||
Impone que todas las requests entrantes tengan correctamente configurado el header `Host`, para proteger contra ataques de HTTP Host Header.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial002.py hl[2,6:8] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial002_py39.py hl[2,6:8] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
* `allowed_hosts` - Una list de nombres de dominio que deberían ser permitidos como nombres de host. Se soportan dominios comodín como `*.example.com` para hacer coincidir subdominios. Para permitir cualquier nombre de host, usa `allowed_hosts=["*"]` u omite el middleware.
|
||||
* `www_redirect` - Si se establece en True, las requests a versiones sin www de los hosts permitidos serán redirigidas a sus equivalentes con www. Por defecto es `True`.
|
||||
|
||||
Si una request entrante no se valida correctamente, se enviará un response `400`.
|
||||
|
||||
## `GZipMiddleware`
|
||||
## `GZipMiddleware` { #gzipmiddleware }
|
||||
|
||||
Maneja responses GZip para cualquier request que incluya `"gzip"` en el header `Accept-Encoding`.
|
||||
|
||||
El middleware manejará tanto responses estándar como en streaming.
|
||||
|
||||
{* ../../docs_src/advanced_middleware/tutorial003.py hl[2,6] *}
|
||||
{* ../../docs_src/advanced_middleware/tutorial003_py39.py hl[2,6] *}
|
||||
|
||||
Se soportan los siguientes argumentos:
|
||||
|
||||
* `minimum_size` - No comprimir con GZip responses que sean más pequeñas que este tamaño mínimo en bytes. Por defecto es `500`.
|
||||
* `compresslevel` - Usado durante la compresión GZip. Es un entero que varía de 1 a 9. Por defecto es `9`. Un valor más bajo resulta en una compresión más rápida pero archivos más grandes, mientras que un valor más alto resulta en una compresión más lenta pero archivos más pequeños.
|
||||
|
||||
## Otros middlewares
|
||||
## Otros middlewares { #other-middlewares }
|
||||
|
||||
Hay muchos otros middlewares ASGI.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# OpenAPI Callbacks
|
||||
# Callbacks de OpenAPI { #openapi-callbacks }
|
||||
|
||||
Podrías crear una API con una *path operation* que podría desencadenar un request a una *API externa* creada por alguien más (probablemente el mismo desarrollador que estaría *usando* tu API).
|
||||
|
||||
El proceso que ocurre cuando tu aplicación API llama a la *API externa* se llama un "callback". Porque el software que escribió el desarrollador externo envía un request a tu API y luego tu API *responde*, enviando un request a una *API externa* (que probablemente fue creada por el mismo desarrollador).
|
||||
El proceso que ocurre cuando tu aplicación API llama a la *API externa* se llama un "callback". Porque el software que escribió el desarrollador externo envía un request a tu API y luego tu API hace un *callback*, enviando un request a una *API externa* (que probablemente fue creada por el mismo desarrollador).
|
||||
|
||||
En este caso, podrías querer documentar cómo esa API externa *debería* verse. Qué *path operation* debería tener, qué cuerpo debería esperar, qué response debería devolver, etc.
|
||||
|
||||
## Una aplicación con callbacks
|
||||
## Una aplicación con callbacks { #an-app-with-callbacks }
|
||||
|
||||
Veamos todo esto con un ejemplo.
|
||||
|
||||
Imagina que desarrollas una aplicación que permite crear facturas.
|
||||
|
||||
Estas facturas tendrán un `id`, `title` (opcional), `customer`, y `total`.
|
||||
Estas facturas tendrán un `id`, `title` (opcional), `customer` y `total`.
|
||||
|
||||
El usuario de tu API (un desarrollador externo) creará una factura en tu API con un request POST.
|
||||
|
||||
@@ -23,15 +23,15 @@ Luego tu API (imaginemos):
|
||||
* Enviará una notificación de vuelta al usuario de la API (el desarrollador externo).
|
||||
* Esto se hará enviando un request POST (desde *tu API*) a alguna *API externa* proporcionada por ese desarrollador externo (este es el "callback").
|
||||
|
||||
## La aplicación normal de **FastAPI**
|
||||
## La aplicación normal de **FastAPI** { #the-normal-fastapi-app }
|
||||
|
||||
Primero veamos cómo sería la aplicación API normal antes de agregar el callback.
|
||||
Primero veamos cómo se vería la aplicación API normal antes de agregar el callback.
|
||||
|
||||
Tendrá una *path operation* que recibirá un cuerpo `Invoice`, y un parámetro de query `callback_url` que contendrá la URL para el callback.
|
||||
|
||||
Esta parte es bastante normal, probablemente ya estés familiarizado con la mayor parte del código:
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -39,9 +39,9 @@ El parámetro de query `callback_url` utiliza un tipo <a href="https://docs.pyda
|
||||
|
||||
///
|
||||
|
||||
Lo único nuevo es el `callbacks=invoices_callback_router.routes` como un argumento para el *decorador de path operation*. Veremos qué es eso a continuación.
|
||||
Lo único nuevo es `callbacks=invoices_callback_router.routes` como un argumento para el *decorador de path operation*. Veremos qué es eso a continuación.
|
||||
|
||||
## Documentar el callback
|
||||
## Documentar el callback { #documenting-the-callback }
|
||||
|
||||
El código real del callback dependerá mucho de tu propia aplicación API.
|
||||
|
||||
@@ -56,7 +56,7 @@ httpx.post(callback_url, json={"description": "Invoice paid", "paid": True})
|
||||
|
||||
Pero posiblemente la parte más importante del callback es asegurarse de que el usuario de tu API (el desarrollador externo) implemente la *API externa* correctamente, de acuerdo con los datos que *tu API* va a enviar en el request body del callback, etc.
|
||||
|
||||
Entonces, lo que haremos a continuación es agregar el código para documentar cómo debería verse esa *API externa* para recibir el callback de *tu API*.
|
||||
Así que, lo que haremos a continuación es agregar el código para documentar cómo debería verse esa *API externa* para recibir el callback de *tu API*.
|
||||
|
||||
Esa documentación aparecerá en la Swagger UI en `/docs` en tu API, y permitirá a los desarrolladores externos saber cómo construir la *API externa*.
|
||||
|
||||
@@ -70,11 +70,11 @@ Cuando implementes el callback tú mismo, podrías usar algo como <a href="https
|
||||
|
||||
///
|
||||
|
||||
## Escribir el código de documentación del callback
|
||||
## Escribe el código de documentación del callback { #write-the-callback-documentation-code }
|
||||
|
||||
Este código no se ejecutará en tu aplicación, solo lo necesitamos para *documentar* cómo debería verse esa *API externa*.
|
||||
|
||||
Pero, ya sabes cómo crear fácilmente documentación automática para una API con **FastAPI**.
|
||||
Pero ya sabes cómo crear fácilmente documentación automática para una API con **FastAPI**.
|
||||
|
||||
Así que vamos a usar ese mismo conocimiento para documentar cómo debería verse la *API externa*... creando la(s) *path operation(s)* que la API externa debería implementar (las que tu API va a llamar).
|
||||
|
||||
@@ -86,29 +86,29 @@ Adoptar temporalmente este punto de vista (del *desarrollador externo*) puede ay
|
||||
|
||||
///
|
||||
|
||||
### Crear un `APIRouter` de callback
|
||||
### Crea un `APIRouter` de callback { #create-a-callback-apirouter }
|
||||
|
||||
Primero crea un nuevo `APIRouter` que contendrá uno o más callbacks.
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[3,25] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *}
|
||||
|
||||
### Crear la *path operation* del callback
|
||||
### Crea la *path operation* del callback { #create-the-callback-path-operation }
|
||||
|
||||
Para crear la *path operation* del callback utiliza el mismo `APIRouter` que creaste anteriormente.
|
||||
Para crear la *path operation* del callback usa el mismo `APIRouter` que creaste arriba.
|
||||
|
||||
Debería verse como una *path operation* normal de FastAPI:
|
||||
|
||||
* Probablemente debería tener una declaración del body que debería recibir, por ejemplo `body: InvoiceEvent`.
|
||||
* Y también podría tener una declaración del response que debería devolver, por ejemplo `response_model=InvoiceEventReceived`.
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[16:18,21:22,28:32] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *}
|
||||
|
||||
Hay 2 diferencias principales respecto a una *path operation* normal:
|
||||
|
||||
* No necesita tener ningún código real, porque tu aplicación nunca llamará a este código. Solo se usa para documentar la *API externa*. Así que, la función podría simplemente tener `pass`.
|
||||
* El *path* puede contener una <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expresión OpenAPI 3</a> (ver más abajo) donde puede usar variables con parámetros y partes del request original enviado a *tu API*.
|
||||
|
||||
### La expresión del path del callback
|
||||
### La expresión del path del callback { #the-callback-path-expression }
|
||||
|
||||
El *path* del callback puede tener una <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression" class="external-link" target="_blank">expresión OpenAPI 3</a> que puede contener partes del request original enviado a *tu API*.
|
||||
|
||||
@@ -134,7 +134,7 @@ con un JSON body de:
|
||||
}
|
||||
```
|
||||
|
||||
luego *tu API* procesará la factura, y en algún momento después, enviará un request de callback al `callback_url` (la *API externa*):
|
||||
luego *tu API* procesará la factura y, en algún momento después, enviará un request de callback al `callback_url` (la *API externa*):
|
||||
|
||||
```
|
||||
https://www.external.org/events/invoices/2expen51ve
|
||||
@@ -163,13 +163,13 @@ Observa cómo la URL del callback utilizada contiene la URL recibida como parám
|
||||
|
||||
///
|
||||
|
||||
### Agregar el router de callback
|
||||
### Agrega el router de callback { #add-the-callback-router }
|
||||
|
||||
En este punto tienes las *path operation(s)* del callback necesarias (las que el *desarrollador externo* debería implementar en la *API externa*) en el router de callback que creaste antes.
|
||||
En este punto tienes las *path operation(s)* del callback necesarias (las que el *desarrollador externo* debería implementar en la *API externa*) en el router de callback que creaste arriba.
|
||||
|
||||
Ahora usa el parámetro `callbacks` en el *decorador de path operation de tu API* para pasar el atributo `.routes` (que en realidad es solo un `list` de rutas/*path operations*) de ese router de callback:
|
||||
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001.py hl[35] *}
|
||||
{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -177,7 +177,7 @@ Observa que no estás pasando el router en sí (`invoices_callback_router`) a `c
|
||||
|
||||
///
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Ahora puedes iniciar tu aplicación e ir a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Webhooks de OpenAPI
|
||||
# Webhooks de OpenAPI { #openapi-webhooks }
|
||||
|
||||
Hay casos donde quieres decirle a los **usuarios** de tu API que tu aplicación podría llamar a *su* aplicación (enviando una request) con algunos datos, normalmente para **notificar** de algún tipo de **evento**.
|
||||
|
||||
@@ -6,7 +6,7 @@ Esto significa que en lugar del proceso normal de tus usuarios enviando requests
|
||||
|
||||
Esto normalmente se llama un **webhook**.
|
||||
|
||||
## Pasos de los webhooks
|
||||
## Pasos de los webhooks { #webhooks-steps }
|
||||
|
||||
El proceso normalmente es que **tú defines** en tu código cuál es el mensaje que enviarás, el **body de la request**.
|
||||
|
||||
@@ -16,7 +16,7 @@ Y **tus usuarios** definen de alguna manera (por ejemplo en un panel web en alg
|
||||
|
||||
Toda la **lógica** sobre cómo registrar los URLs para webhooks y el código para realmente enviar esas requests depende de ti. Lo escribes como quieras en **tu propio código**.
|
||||
|
||||
## Documentando webhooks con **FastAPI** y OpenAPI
|
||||
## Documentando webhooks con **FastAPI** y OpenAPI { #documenting-webhooks-with-fastapi-and-openapi }
|
||||
|
||||
Con **FastAPI**, usando OpenAPI, puedes definir los nombres de estos webhooks, los tipos de operaciones HTTP que tu aplicación puede enviar (por ejemplo, `POST`, `PUT`, etc.) y los **bodies** de las requests que tu aplicación enviaría.
|
||||
|
||||
@@ -28,11 +28,11 @@ Los webhooks están disponibles en OpenAPI 3.1.0 y superiores, soportados por Fa
|
||||
|
||||
///
|
||||
|
||||
## Una aplicación con webhooks
|
||||
## Una aplicación con webhooks { #an-app-with-webhooks }
|
||||
|
||||
Cuando creas una aplicación de **FastAPI**, hay un atributo `webhooks` que puedes usar para definir *webhooks*, de la misma manera que definirías *path operations*, por ejemplo con `@app.webhooks.post()`.
|
||||
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001.py hl[9:13,36:53] *}
|
||||
{* ../../docs_src/openapi_webhooks/tutorial001_py39.py hl[9:13,36:53] *}
|
||||
|
||||
Los webhooks que defines terminarán en el esquema de **OpenAPI** y en la interfaz automática de **documentación**.
|
||||
|
||||
@@ -46,7 +46,7 @@ Nota que con los webhooks en realidad no estás declarando un *path* (como `/ite
|
||||
|
||||
Esto es porque se espera que **tus usuarios** definan el actual **URL path** donde quieren recibir la request del webhook de alguna otra manera (por ejemplo, un panel web).
|
||||
|
||||
### Revisa la documentación
|
||||
### Revisa la documentación { #check-the-docs }
|
||||
|
||||
Ahora puedes iniciar tu app e ir a <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configuración Avanzada de Path Operation
|
||||
# Configuración Avanzada de Path Operation { #path-operation-advanced-configuration }
|
||||
|
||||
## operationId de OpenAPI
|
||||
## operationId de OpenAPI { #openapi-operationid }
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
@@ -10,17 +10,17 @@ Si no eres un "experto" en OpenAPI, probablemente no necesites esto.
|
||||
|
||||
Puedes establecer el `operationId` de OpenAPI para ser usado en tu *path operation* con el parámetro `operation_id`.
|
||||
|
||||
Tienes que asegurarte de que sea único para cada operación.
|
||||
Tendrías que asegurarte de que sea único para cada operación.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py39.py hl[6] *}
|
||||
|
||||
### Usar el nombre de la *función de path operation* como el operationId
|
||||
### Usar el nombre de la *path operation function* como el operationId { #using-the-path-operation-function-name-as-the-operationid }
|
||||
|
||||
Si quieres usar los nombres de las funciones de tus APIs como `operationId`s, puedes iterar sobre todas ellas y sobrescribir el `operation_id` de cada *path operation* usando su `APIRoute.name`.
|
||||
|
||||
Deberías hacerlo después de agregar todas tus *path operations*.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002.py hl[2, 12:21, 24] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py39.py hl[2, 12:21, 24] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -30,29 +30,29 @@ Si llamas manualmente a `app.openapi()`, deberías actualizar los `operationId`s
|
||||
|
||||
/// warning | Advertencia
|
||||
|
||||
Si haces esto, tienes que asegurarte de que cada una de tus *funciones de path operation* tenga un nombre único.
|
||||
Si haces esto, tienes que asegurarte de que cada una de tus *path operation functions* tenga un nombre único.
|
||||
|
||||
Incluso si están en diferentes módulos (archivos de Python).
|
||||
|
||||
///
|
||||
|
||||
## Excluir de OpenAPI
|
||||
## Excluir de OpenAPI { #exclude-from-openapi }
|
||||
|
||||
Para excluir una *path operation* del esquema OpenAPI generado (y por lo tanto, de los sistemas de documentación automática), utiliza el parámetro `include_in_schema` y configúralo en `False`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py39.py hl[6] *}
|
||||
|
||||
## Descripción avanzada desde el docstring
|
||||
## Descripción avanzada desde el docstring { #advanced-description-from-docstring }
|
||||
|
||||
Puedes limitar las líneas usadas del docstring de una *función de path operation* para OpenAPI.
|
||||
Puedes limitar las líneas usadas del docstring de una *path operation function* para OpenAPI.
|
||||
|
||||
Añadir un `\f` (un carácter de separación de página escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
|
||||
Añadir un `\f` (un carácter "form feed" escapado) hace que **FastAPI** trunque la salida usada para OpenAPI en este punto.
|
||||
|
||||
No aparecerá en la documentación, pero otras herramientas (como Sphinx) podrán usar el resto.
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004.py hl[19:29] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *}
|
||||
|
||||
## Responses Adicionales
|
||||
## Responses Adicionales { #additional-responses }
|
||||
|
||||
Probablemente has visto cómo declarar el `response_model` y el `status_code` para una *path operation*.
|
||||
|
||||
@@ -62,11 +62,11 @@ También puedes declarar responses adicionales con sus modelos, códigos de esta
|
||||
|
||||
Hay un capítulo entero en la documentación sobre ello, puedes leerlo en [Responses Adicionales en OpenAPI](additional-responses.md){.internal-link target=_blank}.
|
||||
|
||||
## OpenAPI Extra
|
||||
## OpenAPI Extra { #openapi-extra }
|
||||
|
||||
Cuando declaras una *path operation* en tu aplicación, **FastAPI** genera automáticamente los metadatos relevantes sobre esa *path operation* para incluirlos en el esquema de OpenAPI.
|
||||
|
||||
/// note | Nota
|
||||
/// note | Detalles técnicos
|
||||
|
||||
En la especificación de OpenAPI se llama el <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object" class="external-link" target="_blank">Objeto de Operación</a>.
|
||||
|
||||
@@ -88,11 +88,11 @@ Si solo necesitas declarar responses adicionales, una forma más conveniente de
|
||||
|
||||
Puedes extender el esquema de OpenAPI para una *path operation* usando el parámetro `openapi_extra`.
|
||||
|
||||
### Extensiones de OpenAPI
|
||||
### Extensiones de OpenAPI { #openapi-extensions }
|
||||
|
||||
Este `openapi_extra` puede ser útil, por ejemplo, para declarar [Extensiones de OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions):
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005.py hl[6] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py39.py hl[6] *}
|
||||
|
||||
Si abres la documentación automática de la API, tu extensión aparecerá en la parte inferior de la *path operation* específica.
|
||||
|
||||
@@ -129,7 +129,7 @@ Y si ves el OpenAPI resultante (en `/openapi.json` en tu API), verás tu extensi
|
||||
}
|
||||
```
|
||||
|
||||
### Esquema de *path operation* personalizada de OpenAPI
|
||||
### Esquema de *path operation* personalizada de OpenAPI { #custom-openapi-path-operation-schema }
|
||||
|
||||
El diccionario en `openapi_extra` se combinará profundamente con el esquema de OpenAPI generado automáticamente para la *path operation*.
|
||||
|
||||
@@ -139,61 +139,29 @@ Por ejemplo, podrías decidir leer y validar el request con tu propio código, s
|
||||
|
||||
Podrías hacer eso con `openapi_extra`:
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006.py hl[19:36, 39:40] *}
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py39.py hl[19:36, 39:40] *}
|
||||
|
||||
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el cuerpo del request ni siquiera se <abbr title="converted from some plain format, like bytes, into Python objects">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
|
||||
En este ejemplo, no declaramos ningún modelo Pydantic. De hecho, el request body ni siquiera se <abbr title="converted from some plain format, like bytes, into Python objects - convertido de algún formato plano, como bytes, a objetos de Python">parse</abbr> como JSON, se lee directamente como `bytes`, y la función `magic_data_reader()` sería la encargada de parsearlo de alguna manera.
|
||||
|
||||
Sin embargo, podemos declarar el esquema esperado para el cuerpo del request.
|
||||
Sin embargo, podemos declarar el esquema esperado para el request body.
|
||||
|
||||
### Tipo de contenido personalizado de OpenAPI
|
||||
### Tipo de contenido personalizado de OpenAPI { #custom-openapi-content-type }
|
||||
|
||||
Usando este mismo truco, podrías usar un modelo Pydantic para definir el esquema JSON que luego se incluye en la sección personalizada del esquema OpenAPI para la *path operation*.
|
||||
Usando este mismo truco, podrías usar un modelo Pydantic para definir el JSON Schema que luego se incluye en la sección personalizada del esquema OpenAPI para la *path operation*.
|
||||
|
||||
Y podrías hacer esto incluso si el tipo de datos en el request no es JSON.
|
||||
|
||||
Por ejemplo, en esta aplicación no usamos la funcionalidad integrada de FastAPI para extraer el esquema JSON de los modelos Pydantic ni la validación automática para JSON. De hecho, estamos declarando el tipo de contenido del request como YAML, no JSON:
|
||||
Por ejemplo, en esta aplicación no usamos la funcionalidad integrada de FastAPI para extraer el JSON Schema de los modelos Pydantic ni la validación automática para JSON. De hecho, estamos declarando el tipo de contenido del request como YAML, no JSON:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[15:20, 22] *}
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[17:22, 24] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[17:22, 24] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic el método para obtener el esquema JSON para un modelo se llamaba `Item.schema()`, en la versión 2 de Pydantic, el método se llama `Item.model_json_schema()`.
|
||||
|
||||
///
|
||||
|
||||
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el esquema JSON para los datos que queremos recibir en YAML.
|
||||
Sin embargo, aunque no estamos usando la funcionalidad integrada por defecto, aún estamos usando un modelo Pydantic para generar manualmente el JSON Schema para los datos que queremos recibir en YAML.
|
||||
|
||||
Luego usamos el request directamente, y extraemos el cuerpo como `bytes`. Esto significa que FastAPI ni siquiera intentará parsear la carga útil del request como JSON.
|
||||
|
||||
Y luego en nuestro código, parseamos ese contenido YAML directamente, y nuevamente estamos usando el mismo modelo Pydantic para validar el contenido YAML:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007.py hl[26:33] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_pv1.py hl[26:33] *}
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic el método para parsear y validar un objeto era `Item.parse_obj()`, en la versión 2 de Pydantic, el método se llama `Item.model_validate()`.
|
||||
|
||||
///
|
||||
{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py39.py hl[24:31] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Response - Cambiar Código de Estado
|
||||
# Response - Cambiar Código de Estado { #response-change-status-code }
|
||||
|
||||
Probablemente leíste antes que puedes establecer un [Código de Estado de Response](../tutorial/response-status-code.md){.internal-link target=_blank} por defecto.
|
||||
|
||||
Pero en algunos casos necesitas devolver un código de estado diferente al predeterminado.
|
||||
|
||||
## Caso de uso
|
||||
## Caso de uso { #use-case }
|
||||
|
||||
Por ejemplo, imagina que quieres devolver un código de estado HTTP de "OK" `200` por defecto.
|
||||
|
||||
@@ -14,13 +14,13 @@ Pero todavía quieres poder filtrar y convertir los datos que devuelves con un `
|
||||
|
||||
Para esos casos, puedes usar un parámetro `Response`.
|
||||
|
||||
## Usa un parámetro `Response`
|
||||
## Usa un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *función de path operation* (como puedes hacer para cookies y headers).
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies y headers).
|
||||
|
||||
Y luego puedes establecer el `status_code` en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_change_status_code/tutorial001.py hl[1,9,12] *}
|
||||
{* ../../docs_src/response_change_status_code/tutorial001_py39.py hl[1,9,12] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como lo harías normalmente (un `dict`, un modelo de base de datos, etc.).
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Cookies de Response
|
||||
# Cookies de Response { #response-cookies }
|
||||
|
||||
## Usar un parámetro `Response`
|
||||
## Usar un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function*.
|
||||
|
||||
Y luego puedes establecer cookies en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial002.py hl[1, 8:9] *}
|
||||
{* ../../docs_src/response_cookies/tutorial002_py39.py hl[1, 8:9] *}
|
||||
|
||||
Y entonces puedes devolver cualquier objeto que necesites, como normalmente lo harías (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
@@ -16,7 +16,7 @@ Y si declaraste un `response_model`, todavía se utilizará para filtrar y conve
|
||||
|
||||
También puedes declarar el parámetro `Response` en las dependencias, y establecer cookies (y headers) en ellas.
|
||||
|
||||
## Devolver una `Response` directamente
|
||||
## Devolver una `Response` directamente { #return-a-response-directly }
|
||||
|
||||
También puedes crear cookies al devolver una `Response` directamente en tu código.
|
||||
|
||||
@@ -24,7 +24,7 @@ Para hacer eso, puedes crear un response como se describe en [Devolver un Respon
|
||||
|
||||
Luego establece Cookies en ella, y luego devuélvela:
|
||||
|
||||
{* ../../docs_src/response_cookies/tutorial001.py hl[10:12] *}
|
||||
{* ../../docs_src/response_cookies/tutorial001_py39.py hl[10:12] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -36,7 +36,7 @@ Y también que no estés enviando ningún dato que debería haber sido filtrado
|
||||
|
||||
///
|
||||
|
||||
### Más información
|
||||
### Más información { #more-info }
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Devolver una Response Directamente
|
||||
# Devolver una Response Directamente { #return-a-response-directly }
|
||||
|
||||
Cuando creas una *path operation* en **FastAPI**, normalmente puedes devolver cualquier dato desde ella: un `dict`, una `list`, un modelo de Pydantic, un modelo de base de datos, etc.
|
||||
|
||||
@@ -10,7 +10,7 @@ Pero puedes devolver un `JSONResponse` directamente desde tus *path operations*.
|
||||
|
||||
Esto podría ser útil, por ejemplo, para devolver headers o cookies personalizados.
|
||||
|
||||
## Devolver una `Response`
|
||||
## Devolver una `Response` { #return-a-response }
|
||||
|
||||
De hecho, puedes devolver cualquier `Response` o cualquier subclase de ella.
|
||||
|
||||
@@ -26,7 +26,7 @@ No hará ninguna conversión de datos con los modelos de Pydantic, no convertir
|
||||
|
||||
Esto te da mucha flexibilidad. Puedes devolver cualquier tipo de datos, sobrescribir cualquier declaración o validación de datos, etc.
|
||||
|
||||
## Usar el `jsonable_encoder` en una `Response`
|
||||
## Usar el `jsonable_encoder` en una `Response` { #using-the-jsonable-encoder-in-a-response }
|
||||
|
||||
Como **FastAPI** no realiza cambios en una `Response` que devuelves, tienes que asegurarte de que sus contenidos estén listos para ello.
|
||||
|
||||
@@ -34,9 +34,9 @@ Por ejemplo, no puedes poner un modelo de Pydantic en un `JSONResponse` sin prim
|
||||
|
||||
Para esos casos, puedes usar el `jsonable_encoder` para convertir tus datos antes de pasarlos a un response:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial001.py hl[6:7,21:22] *}
|
||||
{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *}
|
||||
|
||||
/// note | Nota
|
||||
/// note | Detalles técnicos
|
||||
|
||||
También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
@@ -44,7 +44,7 @@ También podrías usar `from starlette.responses import JSONResponse`.
|
||||
|
||||
///
|
||||
|
||||
## Devolver una `Response` personalizada
|
||||
## Devolver una `Response` personalizada { #returning-a-custom-response }
|
||||
|
||||
El ejemplo anterior muestra todas las partes que necesitas, pero aún no es muy útil, ya que podrías haber devuelto el `item` directamente, y **FastAPI** lo colocaría en un `JSONResponse` por ti, convirtiéndolo a un `dict`, etc. Todo eso por defecto.
|
||||
|
||||
@@ -54,9 +54,9 @@ Digamos que quieres devolver un response en <a href="https://en.wikipedia.org/wi
|
||||
|
||||
Podrías poner tu contenido XML en un string, poner eso en un `Response`, y devolverlo:
|
||||
|
||||
{* ../../docs_src/response_directly/tutorial002.py hl[1,18] *}
|
||||
{* ../../docs_src/response_directly/tutorial002_py39.py hl[1,18] *}
|
||||
|
||||
## Notas
|
||||
## Notas { #notes }
|
||||
|
||||
Cuando devuelves una `Response` directamente, sus datos no son validados, convertidos (serializados), ni documentados automáticamente.
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Response Headers
|
||||
# Headers de Response { #response-headers }
|
||||
|
||||
## Usa un parámetro `Response`
|
||||
## Usa un parámetro `Response` { #use-a-response-parameter }
|
||||
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *función de path operation* (como puedes hacer para cookies).
|
||||
Puedes declarar un parámetro de tipo `Response` en tu *path operation function* (como puedes hacer para cookies).
|
||||
|
||||
Y luego puedes establecer headers en ese objeto de response *temporal*.
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial002.py hl[1, 7:8] *}
|
||||
{* ../../docs_src/response_headers/tutorial002_py39.py hl[1, 7:8] *}
|
||||
|
||||
Y luego puedes devolver cualquier objeto que necesites, como harías normalmente (un `dict`, un modelo de base de datos, etc).
|
||||
|
||||
@@ -16,13 +16,13 @@ Y si declaraste un `response_model`, aún se usará para filtrar y convertir el
|
||||
|
||||
También puedes declarar el parámetro `Response` en dependencias y establecer headers (y cookies) en ellas.
|
||||
|
||||
## Retorna una `Response` directamente
|
||||
## Retorna una `Response` directamente { #return-a-response-directly }
|
||||
|
||||
También puedes agregar headers cuando devuelves un `Response` directamente.
|
||||
|
||||
Crea un response como se describe en [Retorna un Response Directamente](response-directly.md){.internal-link target=_blank} y pasa los headers como un parámetro adicional:
|
||||
|
||||
{* ../../docs_src/response_headers/tutorial001.py hl[10:12] *}
|
||||
{* ../../docs_src/response_headers/tutorial001_py39.py hl[10:12] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
@@ -34,8 +34,8 @@ Y como el `Response` se puede usar frecuentemente para establecer headers y cook
|
||||
|
||||
///
|
||||
|
||||
## Headers Personalizados
|
||||
## Headers Personalizados { #custom-headers }
|
||||
|
||||
Ten en cuenta que los headers propietarios personalizados se pueden agregar <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo 'X-'</a>.
|
||||
Ten en cuenta que los headers propietarios personalizados se pueden agregar <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" class="external-link" target="_blank">usando el prefijo `X-`</a>.
|
||||
|
||||
Pero si tienes headers personalizados que quieres que un cliente en un navegador pueda ver, necesitas agregarlos a tus configuraciones de CORS (leer más en [CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md){.internal-link target=_blank}), usando el parámetro `expose_headers` documentado en <a href="https://www.starlette.dev/middleware/#corsmiddleware" class="external-link" target="_blank">la documentación CORS de Starlette</a>.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# HTTP Basic Auth
|
||||
# HTTP Basic Auth { #http-basic-auth }
|
||||
|
||||
Para los casos más simples, puedes usar HTTP Basic Auth.
|
||||
|
||||
@@ -12,7 +12,7 @@ Eso le dice al navegador que muestre el prompt integrado para un nombre de usuar
|
||||
|
||||
Luego, cuando escribes ese nombre de usuario y contraseña, el navegador los envía automáticamente en el header.
|
||||
|
||||
## Simple HTTP Basic Auth
|
||||
## Simple HTTP Basic Auth { #simple-http-basic-auth }
|
||||
|
||||
* Importa `HTTPBasic` y `HTTPBasicCredentials`.
|
||||
* Crea un "esquema de `security`" usando `HTTPBasic`.
|
||||
@@ -26,7 +26,7 @@ Cuando intentas abrir la URL por primera vez (o haces clic en el botón "Execute
|
||||
|
||||
<img src="/img/tutorial/security/image12.png">
|
||||
|
||||
## Revisa el nombre de usuario
|
||||
## Revisa el nombre de usuario { #check-the-username }
|
||||
|
||||
Aquí hay un ejemplo más completo.
|
||||
|
||||
@@ -46,13 +46,13 @@ Esto sería similar a:
|
||||
|
||||
```Python
|
||||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||||
# Return some error
|
||||
# Devuelve algún error
|
||||
...
|
||||
```
|
||||
|
||||
Pero al usar `secrets.compare_digest()` será seguro contra un tipo de ataques llamados "timing attacks".
|
||||
|
||||
### Timing Attacks
|
||||
### Timing attacks { #timing-attacks }
|
||||
|
||||
¿Pero qué es un "timing attack"?
|
||||
|
||||
@@ -80,19 +80,19 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||||
|
||||
Python tendrá que comparar todo `stanleyjobso` en ambos `stanleyjobsox` y `stanleyjobson` antes de darse cuenta de que ambas strings no son las mismas. Así que tomará algunos microsegundos extra para responder "Nombre de usuario o contraseña incorrectos".
|
||||
|
||||
#### El tiempo de respuesta ayuda a los atacantes
|
||||
#### El tiempo de respuesta ayuda a los atacantes { #the-time-to-answer-helps-the-attackers }
|
||||
|
||||
En ese punto, al notar que el servidor tardó algunos microsegundos más en enviar el response "Nombre de usuario o contraseña incorrectos", los atacantes sabrán que acertaron en _algo_, algunas de las letras iniciales eran correctas.
|
||||
|
||||
Y luego pueden intentar de nuevo sabiendo que probablemente es algo más similar a `stanleyjobsox` que a `johndoe`.
|
||||
|
||||
#### Un ataque "profesional"
|
||||
#### Un ataque "profesional" { #a-professional-attack }
|
||||
|
||||
Por supuesto, los atacantes no intentarían todo esto a mano, escribirían un programa para hacerlo, posiblemente con miles o millones de pruebas por segundo. Y obtendrían solo una letra correcta adicional a la vez.
|
||||
|
||||
Pero haciendo eso, en algunos minutos u horas, los atacantes habrían adivinado el nombre de usuario y la contraseña correctos, con la "ayuda" de nuestra aplicación, solo usando el tiempo tomado para responder.
|
||||
|
||||
#### Arréglalo con `secrets.compare_digest()`
|
||||
#### Arréglalo con `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
|
||||
|
||||
Pero en nuestro código estamos usando realmente `secrets.compare_digest()`.
|
||||
|
||||
@@ -100,7 +100,7 @@ En resumen, tomará el mismo tiempo comparar `stanleyjobsox` con `stanleyjobson`
|
||||
|
||||
De esa manera, usando `secrets.compare_digest()` en el código de tu aplicación, será seguro contra todo este rango de ataques de seguridad.
|
||||
|
||||
### Devuelve el error
|
||||
### Devuelve el error { #return-the-error }
|
||||
|
||||
Después de detectar que las credenciales son incorrectas, regresa un `HTTPException` con un código de estado 401 (el mismo que se devuelve cuando no se proporcionan credenciales) y agrega el header `WWW-Authenticate` para que el navegador muestre el prompt de inicio de sesión nuevamente:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Seguridad Avanzada
|
||||
# Seguridad Avanzada { #advanced-security }
|
||||
|
||||
## Funcionalidades Adicionales
|
||||
## Funcionalidades Adicionales { #additional-features }
|
||||
|
||||
Hay algunas funcionalidades extra para manejar la seguridad aparte de las cubiertas en el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
|
||||
@@ -12,8 +12,8 @@ Y es posible que para tu caso de uso, la solución esté en una de ellas.
|
||||
|
||||
///
|
||||
|
||||
## Lee primero el Tutorial
|
||||
## Lee primero el Tutorial { #read-the-tutorial-first }
|
||||
|
||||
Las siguientes secciones asumen que ya leíste el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank}.
|
||||
Las siguientes secciones asumen que ya leíste el [Tutorial - Guía del Usuario: Seguridad](../../tutorial/security/index.md){.internal-link target=_blank} principal.
|
||||
|
||||
Todas están basadas en los mismos conceptos, pero permiten algunas funcionalidades adicionales.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Scopes de OAuth2
|
||||
# Scopes de OAuth2 { #oauth2-scopes }
|
||||
|
||||
Puedes usar scopes de OAuth2 directamente con **FastAPI**, están integrados para funcionar de manera fluida.
|
||||
|
||||
@@ -26,7 +26,7 @@ Pero si sabes que lo necesitas, o tienes curiosidad, sigue leyendo.
|
||||
|
||||
///
|
||||
|
||||
## Scopes de OAuth2 y OpenAPI
|
||||
## Scopes de OAuth2 y OpenAPI { #oauth2-scopes-and-openapi }
|
||||
|
||||
La especificación de OAuth2 define "scopes" como una lista de strings separados por espacios.
|
||||
|
||||
@@ -58,15 +58,15 @@ Para OAuth2 son solo strings.
|
||||
|
||||
///
|
||||
|
||||
## Vista global
|
||||
## Vista global { #global-view }
|
||||
|
||||
Primero, echemos un vistazo rápido a las partes que cambian desde los ejemplos en el **Tutorial - User Guide** principal para [OAuth2 con Password (y hashing), Bearer con tokens JWT](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Ahora usando scopes de OAuth2:
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:125,129:135,140,156] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *}
|
||||
|
||||
Ahora revisemos esos cambios paso a paso.
|
||||
|
||||
## Esquema de seguridad OAuth2
|
||||
## Esquema de seguridad OAuth2 { #oauth2-security-scheme }
|
||||
|
||||
El primer cambio es que ahora estamos declarando el esquema de seguridad OAuth2 con dos scopes disponibles, `me` y `items`.
|
||||
|
||||
@@ -82,7 +82,7 @@ Este es el mismo mecanismo utilizado cuando das permisos al iniciar sesión con
|
||||
|
||||
<img src="/img/tutorial/security/image11.png">
|
||||
|
||||
## Token JWT con scopes
|
||||
## Token JWT con scopes { #jwt-token-with-scopes }
|
||||
|
||||
Ahora, modifica la *path operation* del token para devolver los scopes solicitados.
|
||||
|
||||
@@ -98,9 +98,9 @@ Pero en tu aplicación, por seguridad, deberías asegurarte de añadir solo los
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[156] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *}
|
||||
|
||||
## Declarar scopes en *path operations* y dependencias
|
||||
## Declarar scopes en *path operations* y dependencias { #declare-scopes-in-path-operations-and-dependencies }
|
||||
|
||||
Ahora declaramos que la *path operation* para `/users/me/items/` requiere el scope `items`.
|
||||
|
||||
@@ -124,7 +124,7 @@ Lo estamos haciendo aquí para demostrar cómo **FastAPI** maneja scopes declara
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,140,171] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *}
|
||||
|
||||
/// info | Información Técnica
|
||||
|
||||
@@ -136,7 +136,7 @@ Pero cuando importas `Query`, `Path`, `Depends`, `Security` y otros de `fastapi`
|
||||
|
||||
///
|
||||
|
||||
## Usar `SecurityScopes`
|
||||
## Usar `SecurityScopes` { #use-securityscopes }
|
||||
|
||||
Ahora actualiza la dependencia `get_current_user`.
|
||||
|
||||
@@ -152,7 +152,7 @@ Esta clase `SecurityScopes` es similar a `Request` (`Request` se usó para obten
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *}
|
||||
|
||||
## Usar los `scopes`
|
||||
## Usar los `scopes` { #use-the-scopes }
|
||||
|
||||
El parámetro `security_scopes` será del tipo `SecurityScopes`.
|
||||
|
||||
@@ -166,7 +166,7 @@ En esta excepción, incluimos los scopes requeridos (si los hay) como un string
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *}
|
||||
|
||||
## Verificar el `username` y la forma de los datos
|
||||
## Verificar el `username` y la forma de los datos { #verify-the-username-and-data-shape }
|
||||
|
||||
Verificamos que obtenemos un `username`, y extraemos los scopes.
|
||||
|
||||
@@ -180,17 +180,17 @@ En lugar de, por ejemplo, un `dict`, o algo más, ya que podría romper la aplic
|
||||
|
||||
También verificamos que tenemos un usuario con ese username, y si no, lanzamos esa misma excepción que creamos antes.
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:128] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *}
|
||||
|
||||
## Verificar los `scopes`
|
||||
## Verificar los `scopes` { #verify-the-scopes }
|
||||
|
||||
Ahora verificamos que todos los scopes requeridos, por esta dependencia y todos los dependientes (incluyendo *path operations*), estén incluidos en los scopes proporcionados en el token recibido, de lo contrario, lanzamos una `HTTPException`.
|
||||
|
||||
Para esto, usamos `security_scopes.scopes`, que contiene una `list` con todos estos scopes como `str`.
|
||||
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[129:135] *}
|
||||
{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *}
|
||||
|
||||
## Árbol de dependencias y scopes
|
||||
## Árbol de dependencias y scopes { #dependency-tree-and-scopes }
|
||||
|
||||
Revisemos de nuevo este árbol de dependencias y los scopes.
|
||||
|
||||
@@ -223,7 +223,7 @@ Todo depende de los `scopes` declarados en cada *path operation* y cada dependen
|
||||
|
||||
///
|
||||
|
||||
## Más detalles sobre `SecurityScopes`
|
||||
## Más detalles sobre `SecurityScopes` { #more-details-about-securityscopes }
|
||||
|
||||
Puedes usar `SecurityScopes` en cualquier punto, y en múltiples lugares, no tiene que ser en la dependencia "raíz".
|
||||
|
||||
@@ -233,7 +233,7 @@ Debido a que `SecurityScopes` tendrá todos los scopes declarados por dependient
|
||||
|
||||
Serán verificados independientemente para cada *path operation*.
|
||||
|
||||
## Revisa
|
||||
## Revisa { #check-it }
|
||||
|
||||
Si abres la documentación de la API, puedes autenticarte y especificar qué scopes deseas autorizar.
|
||||
|
||||
@@ -245,7 +245,7 @@ Y si seleccionas el scope `me` pero no el scope `items`, podrás acceder a `/use
|
||||
|
||||
Eso es lo que pasaría a una aplicación de terceros que intentara acceder a una de estas *path operations* con un token proporcionado por un usuario, dependiendo de cuántos permisos el usuario otorgó a la aplicación.
|
||||
|
||||
## Acerca de las integraciones de terceros
|
||||
## Acerca de las integraciones de terceros { #about-third-party-integrations }
|
||||
|
||||
En este ejemplo estamos usando el flujo de OAuth2 "password".
|
||||
|
||||
@@ -269,6 +269,6 @@ Pero al final, están implementando el mismo estándar OAuth2.
|
||||
|
||||
**FastAPI** incluye utilidades para todos estos flujos de autenticación OAuth2 en `fastapi.security.oauth2`.
|
||||
|
||||
## `Security` en `dependencies` del decorador
|
||||
## `Security` en `dependencies` del decorador { #security-in-decorator-dependencies }
|
||||
|
||||
De la misma manera que puedes definir una `list` de `Depends` en el parámetro `dependencies` del decorador (como se explica en [Dependencias en decoradores de path operation](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md){.internal-link target=_blank}), también podrías usar `Security` con `scopes` allí.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Configuraciones y Variables de Entorno
|
||||
# Configuraciones y Variables de Entorno { #settings-and-environment-variables }
|
||||
|
||||
En muchos casos, tu aplicación podría necesitar algunas configuraciones o ajustes externos, por ejemplo, claves secretas, credenciales de base de datos, credenciales para servicios de correo electrónico, etc.
|
||||
|
||||
@@ -12,17 +12,17 @@ Para entender las variables de entorno, puedes leer [Variables de Entorno](../en
|
||||
|
||||
///
|
||||
|
||||
## Tipos y validación
|
||||
## Tipos y validación { #types-and-validation }
|
||||
|
||||
Estas variables de entorno solo pueden manejar strings de texto, ya que son externas a Python y tienen que ser compatibles con otros programas y el resto del sistema (e incluso con diferentes sistemas operativos, como Linux, Windows, macOS).
|
||||
|
||||
Eso significa que cualquier valor leído en Python desde una variable de entorno será un `str`, y cualquier conversión a un tipo diferente o cualquier validación tiene que hacerse en código.
|
||||
|
||||
## Pydantic `Settings`
|
||||
## Pydantic `Settings` { #pydantic-settings }
|
||||
|
||||
Afortunadamente, Pydantic proporciona una gran utilidad para manejar estas configuraciones provenientes de variables de entorno con <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/" class="external-link" target="_blank">Pydantic: Settings management</a>.
|
||||
|
||||
### Instalar `pydantic-settings`
|
||||
### Instalar `pydantic-settings` { #install-pydantic-settings }
|
||||
|
||||
Primero, asegúrate de crear tu [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, actívalo y luego instala el paquete `pydantic-settings`:
|
||||
|
||||
@@ -46,13 +46,7 @@ $ pip install "fastapi[all]"
|
||||
|
||||
</div>
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 venía incluido con el paquete principal. Ahora se distribuye como este paquete independiente para que puedas elegir si instalarlo o no si no necesitas esa funcionalidad.
|
||||
|
||||
///
|
||||
|
||||
### Crear el objeto `Settings`
|
||||
### Crear el objeto `Settings` { #create-the-settings-object }
|
||||
|
||||
Importa `BaseSettings` de Pydantic y crea una sub-clase, muy similar a un modelo de Pydantic.
|
||||
|
||||
@@ -60,23 +54,7 @@ De la misma forma que con los modelos de Pydantic, declaras atributos de clase c
|
||||
|
||||
Puedes usar todas las mismas funcionalidades de validación y herramientas que usas para los modelos de Pydantic, como diferentes tipos de datos y validaciones adicionales con `Field()`.
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/tutorial001.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
/// info | Información
|
||||
|
||||
En Pydantic v1 importarías `BaseSettings` directamente desde `pydantic` en lugar de desde `pydantic_settings`.
|
||||
|
||||
///
|
||||
|
||||
{* ../../docs_src/settings/tutorial001_pv1.py hl[2,5:8,11] *}
|
||||
|
||||
////
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[2,5:8,11] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -84,17 +62,17 @@ Si quieres algo rápido para copiar y pegar, no uses este ejemplo, usa el últim
|
||||
|
||||
///
|
||||
|
||||
Luego, cuando creas una instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
|
||||
Luego, cuando creas un instance de esa clase `Settings` (en este caso, en el objeto `settings`), Pydantic leerá las variables de entorno de una manera indiferente a mayúsculas y minúsculas, por lo que una variable en mayúsculas `APP_NAME` aún será leída para el atributo `app_name`.
|
||||
|
||||
Luego convertirá y validará los datos. Así que, cuando uses ese objeto `settings`, tendrás datos de los tipos que declaraste (por ejemplo, `items_per_user` será un `int`).
|
||||
|
||||
### Usar el `settings`
|
||||
### Usar el `settings` { #use-the-settings }
|
||||
|
||||
Luego puedes usar el nuevo objeto `settings` en tu aplicación:
|
||||
|
||||
{* ../../docs_src/settings/tutorial001.py hl[18:20] *}
|
||||
{* ../../docs_src/settings/tutorial001_py39.py hl[18:20] *}
|
||||
|
||||
### Ejecutar el servidor
|
||||
### Ejecutar el servidor { #run-the-server }
|
||||
|
||||
Luego, ejecutarías el servidor pasando las configuraciones como variables de entorno, por ejemplo, podrías establecer un `ADMIN_EMAIL` y `APP_NAME` con:
|
||||
|
||||
@@ -110,7 +88,7 @@ $ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.p
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
Para establecer múltiples variables de entorno para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
|
||||
Para establecer múltiples env vars para un solo comando, simplemente sepáralas con un espacio y ponlas todas antes del comando.
|
||||
|
||||
///
|
||||
|
||||
@@ -120,17 +98,17 @@ El `app_name` sería `"ChimichangApp"`.
|
||||
|
||||
Y el `items_per_user` mantendría su valor por defecto de `50`.
|
||||
|
||||
## Configuraciones en otro módulo
|
||||
## Configuraciones en otro módulo { #settings-in-another-module }
|
||||
|
||||
Podrías poner esas configuraciones en otro archivo de módulo como viste en [Aplicaciones Más Grandes - Múltiples Archivos](../tutorial/bigger-applications.md){.internal-link target=_blank}.
|
||||
|
||||
Por ejemplo, podrías tener un archivo `config.py` con:
|
||||
|
||||
{* ../../docs_src/settings/app01/config.py *}
|
||||
{* ../../docs_src/settings/app01_py39/config.py *}
|
||||
|
||||
Y luego usarlo en un archivo `main.py`:
|
||||
|
||||
{* ../../docs_src/settings/app01/main.py hl[3,11:13] *}
|
||||
{* ../../docs_src/settings/app01_py39/main.py hl[3,11:13] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -138,21 +116,21 @@ También necesitarías un archivo `__init__.py` como viste en [Aplicaciones Más
|
||||
|
||||
///
|
||||
|
||||
## Configuraciones en una dependencia
|
||||
## Configuraciones en una dependencia { #settings-in-a-dependency }
|
||||
|
||||
En algunas ocasiones podría ser útil proporcionar las configuraciones desde una dependencia, en lugar de tener un objeto global con `settings` que se use en todas partes.
|
||||
|
||||
Esto podría ser especialmente útil durante las pruebas, ya que es muy fácil sobrescribir una dependencia con tus propias configuraciones personalizadas.
|
||||
|
||||
### El archivo de configuración
|
||||
### El archivo de configuración { #the-config-file }
|
||||
|
||||
Proveniente del ejemplo anterior, tu archivo `config.py` podría verse como:
|
||||
|
||||
{* ../../docs_src/settings/app02/config.py hl[10] *}
|
||||
{* ../../docs_src/settings/app02_an_py39/config.py hl[10] *}
|
||||
|
||||
Nota que ahora no creamos una instance por defecto `settings = Settings()`.
|
||||
Nota que ahora no creamos un instance por defecto `settings = Settings()`.
|
||||
|
||||
### El archivo principal de la app
|
||||
### El archivo principal de la app { #the-main-app-file }
|
||||
|
||||
Ahora creamos una dependencia que devuelve un nuevo `config.Settings()`.
|
||||
|
||||
@@ -170,17 +148,17 @@ Y luego podemos requerirlo desde la *path operation function* como una dependenc
|
||||
|
||||
{* ../../docs_src/settings/app02_an_py39/main.py hl[17,19:21] *}
|
||||
|
||||
### Configuraciones y pruebas
|
||||
### Configuraciones y pruebas { #settings-and-testing }
|
||||
|
||||
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al sobrescribir una dependencia para `get_settings`:
|
||||
Luego sería muy fácil proporcionar un objeto de configuraciones diferente durante las pruebas al crear una sobrescritura de dependencia para `get_settings`:
|
||||
|
||||
{* ../../docs_src/settings/app02/test_main.py hl[9:10,13,21] *}
|
||||
{* ../../docs_src/settings/app02_an_py39/test_main.py hl[9:10,13,21] *}
|
||||
|
||||
En la dependencia sobreescrita establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
|
||||
En la sobrescritura de dependencia establecemos un nuevo valor para el `admin_email` al crear el nuevo objeto `Settings`, y luego devolvemos ese nuevo objeto.
|
||||
|
||||
Luego podemos probar que se está usando.
|
||||
|
||||
## Leer un archivo `.env`
|
||||
## Leer un archivo `.env` { #reading-a-env-file }
|
||||
|
||||
Si tienes muchas configuraciones que posiblemente cambien mucho, tal vez en diferentes entornos, podría ser útil ponerlos en un archivo y luego leerlos desde allí como si fueran variables de entorno.
|
||||
|
||||
@@ -202,7 +180,7 @@ Para que esto funcione, necesitas `pip install python-dotenv`.
|
||||
|
||||
///
|
||||
|
||||
### El archivo `.env`
|
||||
### El archivo `.env` { #the-env-file }
|
||||
|
||||
Podrías tener un archivo `.env` con:
|
||||
|
||||
@@ -211,13 +189,11 @@ ADMIN_EMAIL="deadpool@example.com"
|
||||
APP_NAME="ChimichangApp"
|
||||
```
|
||||
|
||||
### Leer configuraciones desde `.env`
|
||||
### Leer configuraciones desde `.env` { #read-settings-from-env }
|
||||
|
||||
Y luego actualizar tu `config.py` con:
|
||||
|
||||
//// tab | Pydantic v2
|
||||
|
||||
{* ../../docs_src/settings/app03_an/config.py hl[9] *}
|
||||
{* ../../docs_src/settings/app03_an_py39/config.py hl[9] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
@@ -225,29 +201,9 @@ El atributo `model_config` se usa solo para configuración de Pydantic. Puedes l
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
//// tab | Pydantic v1
|
||||
|
||||
{* ../../docs_src/settings/app03_an/config_pv1.py hl[9:10] *}
|
||||
|
||||
/// tip | Consejo
|
||||
|
||||
La clase `Config` se usa solo para configuración de Pydantic. Puedes leer más en <a href="https://docs.pydantic.dev/1.10/usage/model_config/" class="external-link" target="_blank">Pydantic Model Config</a>.
|
||||
|
||||
///
|
||||
|
||||
////
|
||||
|
||||
/// info | Información
|
||||
|
||||
En la versión 1 de Pydantic la configuración se hacía en una clase interna `Config`, en la versión 2 de Pydantic se hace en un atributo `model_config`. Este atributo toma un `dict`, y para obtener autocompletado y errores en línea, puedes importar y usar `SettingsConfigDict` para definir ese `dict`.
|
||||
|
||||
///
|
||||
|
||||
Aquí definimos la configuración `env_file` dentro de tu clase Pydantic `Settings`, y establecemos el valor en el nombre del archivo con el archivo dotenv que queremos usar.
|
||||
|
||||
### Creando el `Settings` solo una vez con `lru_cache`
|
||||
### Creando el `Settings` solo una vez con `lru_cache` { #creating-the-settings-only-once-with-lru-cache }
|
||||
|
||||
Leer un archivo desde el disco es normalmente una operación costosa (lenta), por lo que probablemente quieras hacerlo solo una vez y luego reutilizar el mismo objeto de configuraciones, en lugar de leerlo para cada request.
|
||||
|
||||
@@ -274,7 +230,7 @@ Pero como estamos usando el decorador `@lru_cache` encima, el objeto `Settings`
|
||||
|
||||
Entonces, para cualquier llamada subsiguiente de `get_settings()` en las dependencias de los próximos requests, en lugar de ejecutar el código interno de `get_settings()` y crear un nuevo objeto `Settings`, devolverá el mismo objeto que fue devuelto en la primera llamada, una y otra vez.
|
||||
|
||||
#### Detalles Técnicos de `lru_cache`
|
||||
#### Detalles Técnicos de `lru_cache` { #lru-cache-technical-details }
|
||||
|
||||
`@lru_cache` modifica la función que decora para devolver el mismo valor que se devolvió la primera vez, en lugar de calcularlo nuevamente, ejecutando el código de la función cada vez.
|
||||
|
||||
@@ -331,13 +287,13 @@ participant execute as Ejecutar función
|
||||
end
|
||||
```
|
||||
|
||||
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devolverá el mismo valor.
|
||||
En el caso de nuestra dependencia `get_settings()`, la función ni siquiera toma argumentos, por lo que siempre devuelve el mismo valor.
|
||||
|
||||
De esa manera, se comporta casi como si fuera solo una variable global. Pero como usa una función de dependencia, entonces podemos sobrescribirla fácilmente para las pruebas.
|
||||
|
||||
`@lru_cache` es parte de `functools`, que es parte del library estándar de Python, puedes leer más sobre él en las <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">docs de Python para `@lru_cache`</a>.
|
||||
`@lru_cache` es parte de `functools`, que es parte del paquete estándar de Python, puedes leer más sobre él en las <a href="https://docs.python.org/3/library/functools.html#functools.lru_cache" class="external-link" target="_blank">docs de Python para `@lru_cache`</a>.
|
||||
|
||||
## Resumen
|
||||
## Resumen { #recap }
|
||||
|
||||
Puedes usar Pydantic Settings para manejar las configuraciones o ajustes de tu aplicación, con todo el poder de los modelos de Pydantic.
|
||||
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
# Sub Aplicaciones - Mounts
|
||||
# Sub Aplicaciones - Mounts { #sub-applications-mounts }
|
||||
|
||||
Si necesitas tener dos aplicaciones de **FastAPI** independientes, cada una con su propio OpenAPI independiente y su propia interfaz de docs, puedes tener una aplicación principal y "montar" una (o más) sub-aplicación(es).
|
||||
|
||||
## Montar una aplicación **FastAPI**
|
||||
## Montar una aplicación **FastAPI** { #mounting-a-fastapi-application }
|
||||
|
||||
"Montar" significa añadir una aplicación completamente "independiente" en un path específico, que luego se encarga de manejar todo bajo ese path, con las _path operations_ declaradas en esa sub-aplicación.
|
||||
|
||||
### Aplicación de nivel superior
|
||||
### Aplicación de nivel superior { #top-level-application }
|
||||
|
||||
Primero, crea la aplicación principal de nivel superior de **FastAPI**, y sus *path operations*:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[3, 6:8] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[3, 6:8] *}
|
||||
|
||||
### Sub-aplicación
|
||||
### Sub-aplicación { #sub-application }
|
||||
|
||||
Luego, crea tu sub-aplicación, y sus *path operations*.
|
||||
|
||||
Esta sub-aplicación es solo otra aplicación estándar de FastAPI, pero es la que se "montará":
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 14:16] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 14:16] *}
|
||||
|
||||
### Montar la sub-aplicación
|
||||
### Montar la sub-aplicación { #mount-the-sub-application }
|
||||
|
||||
En tu aplicación de nivel superior, `app`, monta la sub-aplicación, `subapi`.
|
||||
|
||||
En este caso, se montará en el path `/subapi`:
|
||||
|
||||
{* ../../docs_src/sub_applications/tutorial001.py hl[11, 19] *}
|
||||
{* ../../docs_src/sub_applications/tutorial001_py39.py hl[11, 19] *}
|
||||
|
||||
### Revisa la documentación automática de la API
|
||||
### Revisa la documentación automática de la API { #check-the-automatic-api-docs }
|
||||
|
||||
Ahora, ejecuta el comando `fastapi` con tu archivo:
|
||||
|
||||
@@ -56,7 +56,7 @@ Verás la documentación automática de la API para la sub-aplicación, incluyen
|
||||
|
||||
Si intentas interactuar con cualquiera de las dos interfaces de usuario, funcionarán correctamente, porque el navegador podrá comunicarse con cada aplicación o sub-aplicación específica.
|
||||
|
||||
### Detalles Técnicos: `root_path`
|
||||
### Detalles Técnicos: `root_path` { #technical-details-root-path }
|
||||
|
||||
Cuando montas una sub-aplicación como se describe arriba, FastAPI se encargará de comunicar el path de montaje para la sub-aplicación usando un mecanismo de la especificación ASGI llamado `root_path`.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Plantillas
|
||||
# Plantillas { #templates }
|
||||
|
||||
Puedes usar cualquier motor de plantillas que desees con **FastAPI**.
|
||||
|
||||
@@ -6,7 +6,7 @@ Una elección común es Jinja2, el mismo que usa Flask y otras herramientas.
|
||||
|
||||
Hay utilidades para configurarlo fácilmente que puedes usar directamente en tu aplicación de **FastAPI** (proporcionadas por Starlette).
|
||||
|
||||
## Instalar dependencias
|
||||
## Instala dependencias { #install-dependencies }
|
||||
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `jinja2`:
|
||||
|
||||
@@ -20,14 +20,14 @@ $ pip install jinja2
|
||||
|
||||
</div>
|
||||
|
||||
## Usando `Jinja2Templates`
|
||||
## Usando `Jinja2Templates` { #using-jinja2templates }
|
||||
|
||||
* Importa `Jinja2Templates`.
|
||||
* Crea un objeto `templates` que puedas reutilizar más tarde.
|
||||
* Declara un parámetro `Request` en la *path operation* que devolverá una plantilla.
|
||||
* Usa los `templates` que creaste para renderizar y devolver un `TemplateResponse`, pasa el nombre de la plantilla, el objeto de request, y un diccionario "context" con pares clave-valor que se usarán dentro de la plantilla Jinja2.
|
||||
|
||||
{* ../../docs_src/templates/tutorial001.py hl[4,11,15:18] *}
|
||||
{* ../../docs_src/templates/tutorial001_py39.py hl[4,11,15:18] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
@@ -51,7 +51,7 @@ También podrías usar `from starlette.templating import Jinja2Templates`.
|
||||
|
||||
///
|
||||
|
||||
## Escribiendo plantillas
|
||||
## Escribiendo plantillas { #writing-templates }
|
||||
|
||||
Luego puedes escribir una plantilla en `templates/item.html` con, por ejemplo:
|
||||
|
||||
@@ -59,7 +59,7 @@ Luego puedes escribir una plantilla en `templates/item.html` con, por ejemplo:
|
||||
{!../../docs_src/templates/templates/item.html!}
|
||||
```
|
||||
|
||||
### Valores de Contexto de la Plantilla
|
||||
### Valores de Contexto de la Plantilla { #template-context-values }
|
||||
|
||||
En el HTML que contiene:
|
||||
|
||||
@@ -83,7 +83,7 @@ Por ejemplo, con un ID de `42`, esto se renderizaría como:
|
||||
Item ID: 42
|
||||
```
|
||||
|
||||
### Argumentos de la Plantilla `url_for`
|
||||
### Argumentos de la Plantilla `url_for` { #template-url-for-arguments }
|
||||
|
||||
También puedes usar `url_for()` dentro de la plantilla, toma como argumentos los mismos que usaría tu *path operation function*.
|
||||
|
||||
@@ -105,7 +105,7 @@ Por ejemplo, con un ID de `42`, esto se renderizaría como:
|
||||
<a href="/items/42">
|
||||
```
|
||||
|
||||
## Plantillas y archivos estáticos
|
||||
## Plantillas y archivos estáticos { #templates-and-static-files }
|
||||
|
||||
También puedes usar `url_for()` dentro de la plantilla, y usarlo, por ejemplo, con los `StaticFiles` que montaste con el `name="static"`.
|
||||
|
||||
@@ -121,6 +121,6 @@ En este ejemplo, enlazaría a un archivo CSS en `static/styles.css` con:
|
||||
|
||||
Y porque estás usando `StaticFiles`, ese archivo CSS sería servido automáticamente por tu aplicación de **FastAPI** en la URL `/static/styles.css`.
|
||||
|
||||
## Más detalles
|
||||
## Más detalles { #more-details }
|
||||
|
||||
Para más detalles, incluyendo cómo testear plantillas, revisa <a href="https://www.starlette.dev/templates/" class="external-link" target="_blank">la documentación de Starlette sobre plantillas</a>.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Probando Dependencias con Overrides
|
||||
# Probando Dependencias con Overrides { #testing-dependencies-with-overrides }
|
||||
|
||||
## Sobrescribir dependencias durante las pruebas
|
||||
## Sobrescribir dependencias durante las pruebas { #overriding-dependencies-during-testing }
|
||||
|
||||
Hay algunos escenarios donde podrías querer sobrescribir una dependencia durante las pruebas.
|
||||
|
||||
@@ -8,7 +8,7 @@ No quieres que la dependencia original se ejecute (ni ninguna de las sub-depende
|
||||
|
||||
En cambio, quieres proporcionar una dependencia diferente que se usará solo durante las pruebas (posiblemente solo algunas pruebas específicas), y que proporcionará un valor que pueda ser usado donde se usó el valor de la dependencia original.
|
||||
|
||||
### Casos de uso: servicio externo
|
||||
### Casos de uso: servicio externo { #use-cases-external-service }
|
||||
|
||||
Un ejemplo podría ser que tienes un proveedor de autenticación externo al que necesitas llamar.
|
||||
|
||||
@@ -20,7 +20,7 @@ Probablemente quieras probar el proveedor externo una vez, pero no necesariament
|
||||
|
||||
En este caso, puedes sobrescribir la dependencia que llama a ese proveedor y usar una dependencia personalizada que devuelva un usuario de prueba, solo para tus tests.
|
||||
|
||||
### Usa el atributo `app.dependency_overrides`
|
||||
### Usa el atributo `app.dependency_overrides` { #use-the-app-dependency-overrides-attribute }
|
||||
|
||||
Para estos casos, tu aplicación **FastAPI** tiene un atributo `app.dependency_overrides`, es un simple `dict`.
|
||||
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Testing Events: startup - shutdown
|
||||
# Eventos de testing: lifespan y startup - shutdown { #testing-events-lifespan-and-startup-shutdown }
|
||||
|
||||
Cuando necesitas que tus manejadores de eventos (`startup` y `shutdown`) se ejecuten en tus tests, puedes usar el `TestClient` con un statement `with`:
|
||||
Cuando necesitas que `lifespan` se ejecute en tus tests, puedes usar el `TestClient` con un statement `with`:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003.py hl[9:12,20:24] *}
|
||||
{* ../../docs_src/app_testing/tutorial004_py39.py hl[9:15,18,27:28,30:32,41:43] *}
|
||||
|
||||
|
||||
Puedes leer más detalles sobre ["Ejecutar lifespan en tests en el sitio oficial de documentación de Starlette."](https://www.starlette.dev/lifespan/#running-lifespan-in-tests)
|
||||
|
||||
Para los eventos obsoletos `startup` y `shutdown`, puedes usar el `TestClient` así:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial003_py39.py hl[9:12,20:24] *}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Probando WebSockets
|
||||
# Probando WebSockets { #testing-websockets }
|
||||
|
||||
Puedes usar el mismo `TestClient` para probar WebSockets.
|
||||
|
||||
Para esto, usas el `TestClient` en un statement `with`, conectándote al WebSocket:
|
||||
|
||||
{* ../../docs_src/app_testing/tutorial002.py hl[27:31] *}
|
||||
{* ../../docs_src/app_testing/tutorial002_py39.py hl[27:31] *}
|
||||
|
||||
/// note | Nota
|
||||
|
||||
Para más detalles, revisa la documentación de Starlette sobre <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">probando sesiones WebSocket</a>.
|
||||
Para más detalles, revisa la documentación de Starlette sobre <a href="https://www.starlette.dev/testclient/#testing-websocket-sessions" class="external-link" target="_blank">probar WebSockets</a>.
|
||||
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Usar el Request Directamente
|
||||
# Usar el Request Directamente { #using-the-request-directly }
|
||||
|
||||
Hasta ahora, has estado declarando las partes del request que necesitas con sus tipos.
|
||||
|
||||
@@ -13,7 +13,7 @@ Y al hacerlo, **FastAPI** está validando esos datos, convirtiéndolos y generan
|
||||
|
||||
Pero hay situaciones donde podrías necesitar acceder al objeto `Request` directamente.
|
||||
|
||||
## Detalles sobre el objeto `Request`
|
||||
## Detalles sobre el objeto `Request` { #details-about-the-request-object }
|
||||
|
||||
Como **FastAPI** es en realidad **Starlette** por debajo, con una capa de varias herramientas encima, puedes usar el objeto <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">`Request`</a> de Starlette directamente cuando lo necesites.
|
||||
|
||||
@@ -23,13 +23,13 @@ Aunque cualquier otro parámetro declarado normalmente (por ejemplo, el cuerpo c
|
||||
|
||||
Pero hay casos específicos donde es útil obtener el objeto `Request`.
|
||||
|
||||
## Usa el objeto `Request` directamente
|
||||
## Usa el objeto `Request` directamente { #use-the-request-object-directly }
|
||||
|
||||
Imaginemos que quieres obtener la dirección IP/host del cliente dentro de tu *path operation function*.
|
||||
|
||||
Para eso necesitas acceder al request directamente.
|
||||
|
||||
{* ../../docs_src/using_request_directly/tutorial001.py hl[1,7:8] *}
|
||||
{* ../../docs_src/using_request_directly/tutorial001_py39.py hl[1,7:8] *}
|
||||
|
||||
Al declarar un parámetro de *path operation function* con el tipo siendo `Request`, **FastAPI** sabrá pasar el `Request` en ese parámetro.
|
||||
|
||||
@@ -43,7 +43,7 @@ De la misma manera, puedes declarar cualquier otro parámetro como normalmente,
|
||||
|
||||
///
|
||||
|
||||
## Documentación de `Request`
|
||||
## Documentación de `Request` { #request-documentation }
|
||||
|
||||
Puedes leer más detalles sobre el <a href="https://www.starlette.dev/requests/" class="external-link" target="_blank">objeto `Request` en el sitio de documentación oficial de Starlette</a>.
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# WebSockets
|
||||
# WebSockets { #websockets }
|
||||
|
||||
Puedes usar <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" class="external-link" target="_blank">WebSockets</a> con **FastAPI**.
|
||||
|
||||
## Instalar `WebSockets`
|
||||
## Instalar `websockets` { #install-websockets }
|
||||
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `websockets`:
|
||||
Asegúrate de crear un [entorno virtual](../virtual-environments.md){.internal-link target=_blank}, activarlo e instalar `websockets` (un paquete de Python que facilita usar el protocolo "WebSocket"):
|
||||
|
||||
<div class="termy">
|
||||
|
||||
@@ -16,9 +16,9 @@ $ pip install websockets
|
||||
|
||||
</div>
|
||||
|
||||
## Cliente WebSockets
|
||||
## Cliente WebSockets { #websockets-client }
|
||||
|
||||
### En producción
|
||||
### En producción { #in-production }
|
||||
|
||||
En tu sistema de producción, probablemente tengas un frontend creado con un framework moderno como React, Vue.js o Angular.
|
||||
|
||||
@@ -38,13 +38,13 @@ En producción tendrías una de las opciones anteriores.
|
||||
|
||||
Pero es la forma más sencilla de enfocarse en el lado del servidor de WebSockets y tener un ejemplo funcional:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[2,6:38,41:43] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[2,6:38,41:43] *}
|
||||
|
||||
## Crear un `websocket`
|
||||
## Crear un `websocket` { #create-a-websocket }
|
||||
|
||||
En tu aplicación de **FastAPI**, crea un `websocket`:
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[1,46:47] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[1,46:47] *}
|
||||
|
||||
/// note | Detalles Técnicos
|
||||
|
||||
@@ -54,15 +54,15 @@ También podrías usar `from starlette.websockets import WebSocket`.
|
||||
|
||||
///
|
||||
|
||||
## Esperar mensajes y enviar mensajes
|
||||
## Esperar mensajes y enviar mensajes { #await-for-messages-and-send-messages }
|
||||
|
||||
En tu ruta de WebSocket puedes `await` para recibir mensajes y enviar mensajes.
|
||||
|
||||
{* ../../docs_src/websockets/tutorial001.py hl[48:52] *}
|
||||
{* ../../docs_src/websockets/tutorial001_py39.py hl[48:52] *}
|
||||
|
||||
Puedes recibir y enviar datos binarios, de texto y JSON.
|
||||
|
||||
## Pruébalo
|
||||
## Pruébalo { #try-it }
|
||||
|
||||
Si tu archivo se llama `main.py`, ejecuta tu aplicación con:
|
||||
|
||||
@@ -96,7 +96,7 @@ Puedes enviar (y recibir) muchos mensajes:
|
||||
|
||||
Y todos usarán la misma conexión WebSocket.
|
||||
|
||||
## Usando `Depends` y otros
|
||||
## Usando `Depends` y otros { #using-depends-and-others }
|
||||
|
||||
En endpoints de WebSocket puedes importar desde `fastapi` y usar:
|
||||
|
||||
@@ -119,7 +119,7 @@ Puedes usar un código de cierre de los <a href="https://tools.ietf.org/html/rfc
|
||||
|
||||
///
|
||||
|
||||
### Prueba los WebSockets con dependencias
|
||||
### Prueba los WebSockets con dependencias { #try-the-websockets-with-dependencies }
|
||||
|
||||
Si tu archivo se llama `main.py`, ejecuta tu aplicación con:
|
||||
|
||||
@@ -150,7 +150,7 @@ Con eso puedes conectar el WebSocket y luego enviar y recibir mensajes:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## Manejar desconexiones y múltiples clientes
|
||||
## Manejar desconexiones y múltiples clientes { #handling-disconnections-and-multiple-clients }
|
||||
|
||||
Cuando una conexión de WebSocket se cierra, el `await websocket.receive_text()` lanzará una excepción `WebSocketDisconnect`, que puedes capturar y manejar como en este ejemplo.
|
||||
|
||||
@@ -178,7 +178,7 @@ Si necesitas algo fácil de integrar con FastAPI pero que sea más robusto, sopo
|
||||
|
||||
///
|
||||
|
||||
## Más información
|
||||
## Más información { #more-info }
|
||||
|
||||
Para aprender más sobre las opciones, revisa la documentación de Starlette para:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Incluyendo WSGI - Flask, Django, otros
|
||||
# Incluyendo WSGI - Flask, Django, otros { #including-wsgi-flask-django-others }
|
||||
|
||||
Puedes montar aplicaciones WSGI como viste con [Sub Aplicaciones - Mounts](sub-applications.md){.internal-link target=_blank}, [Detrás de un Proxy](behind-a-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
Para eso, puedes usar `WSGIMiddleware` y usarlo para envolver tu aplicación WSGI, por ejemplo, Flask, Django, etc.
|
||||
|
||||
## Usando `WSGIMiddleware`
|
||||
## Usando `WSGIMiddleware` { #using-wsgimiddleware }
|
||||
|
||||
Necesitas importar `WSGIMiddleware`.
|
||||
|
||||
@@ -12,9 +12,9 @@ Luego envuelve la aplicación WSGI (p. ej., Flask) con el middleware.
|
||||
|
||||
Y luego móntala bajo un path.
|
||||
|
||||
{* ../../docs_src/wsgi/tutorial001.py hl[2:3,3] *}
|
||||
{* ../../docs_src/wsgi/tutorial001_py39.py hl[2:3,3] *}
|
||||
|
||||
## Revisa
|
||||
## Revisa { #check-it }
|
||||
|
||||
Ahora, cada request bajo el path `/v1/` será manejado por la aplicación Flask.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user