committed by
GitHub
5 changed files with 180 additions and 44 deletions
@ -0,0 +1,58 @@ |
|||||
|
# Utilizando o Request diretamente |
||||
|
|
||||
|
Até agora você declarou as partes da requisição que você precisa utilizando os seus tipos. |
||||
|
|
||||
|
Obtendo dados de: |
||||
|
|
||||
|
* Os parâmetros das rotas. |
||||
|
* Cabeçalhos (*Headers*). |
||||
|
* Cookies. |
||||
|
* etc. |
||||
|
|
||||
|
E ao fazer isso, o **FastAPI** está validando as informações, convertendo-as e gerando documentação para a sua API automaticamente. |
||||
|
|
||||
|
Porém há situações em que você possa precisar acessar o objeto `Request` diretamente. |
||||
|
|
||||
|
## Detalhes sobre o objeto `Request` |
||||
|
|
||||
|
Como o **FastAPI** é na verdade o **Starlette** por baixo, com camadas de diversas funcionalidades por cima, você pode utilizar o objeto <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request`</a> do Starlette diretamente quando precisar. |
||||
|
|
||||
|
Isso significaria também que se você obtiver informações do objeto `Request` diretamente (ler o corpo da requisição por exemplo), as informações não serão validadas, convertidas ou documentadas (com o OpenAPI, para a interface de usuário automática da API) pelo FastAPI. |
||||
|
|
||||
|
Embora qualquer outro parâmetro declarado normalmente (o corpo da requisição com um modelo Pydantic, por exemplo) ainda seria validado, convertido, anotado, etc. |
||||
|
|
||||
|
Mas há situações específicas onde é útil utilizar o objeto `Request`. |
||||
|
|
||||
|
## Utilize o objeto `Request` diretamente |
||||
|
|
||||
|
Vamos imaginar que você deseja obter o endereço de IP/host do cliente dentro da sua *função de operação de rota*. |
||||
|
|
||||
|
Para isso você precisa acessar a requisição diretamente. |
||||
|
|
||||
|
```Python hl_lines="1 7-8" |
||||
|
{!../../../docs_src/using_request_directly/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Ao declarar o parâmetro com o tipo sendo um `Request` em sua *função de operação de rota*, o **FastAPI** saberá como passar o `Request` neste parâmetro. |
||||
|
|
||||
|
/// tip | "Dica" |
||||
|
|
||||
|
Note que neste caso, nós estamos declarando o parâmetro da rota ao lado do parâmetro da requisição. |
||||
|
|
||||
|
Assim, o parâmetro da rota será extraído, validado, convertido para o tipo especificado e anotado com OpenAPI. |
||||
|
|
||||
|
Do mesmo jeito, você pode declarar qualquer outro parâmetro normalmente, e além disso, obter o `Request` também. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Documentação do `Request` |
||||
|
|
||||
|
Você pode ler mais sobre os detalhes do objeto <a href="https://www.starlette.io/requests/" class="external-link" target="_blank">`Request` no site da documentação oficial do Starlette.</a>. |
||||
|
|
||||
|
/// note | "Detalhes Técnicos" |
||||
|
|
||||
|
Você também pode utilizar `from starlette.requests import Request`. |
||||
|
|
||||
|
O **FastAPI** fornece isso diretamente apenas como uma conveniência para você, o desenvolvedor. Mas ele vem diretamente do Starlette. |
||||
|
|
||||
|
/// |
@ -1,31 +0,0 @@ |
|||||
import logging |
|
||||
import sys |
|
||||
|
|
||||
from github import Github |
|
||||
from pydantic import SecretStr |
|
||||
from pydantic_settings import BaseSettings |
|
||||
|
|
||||
|
|
||||
class Settings(BaseSettings): |
|
||||
github_repository: str |
|
||||
github_token: SecretStr |
|
||||
deploy_url: str |
|
||||
commit_sha: str |
|
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
|
||||
logging.basicConfig(level=logging.INFO) |
|
||||
settings = Settings() |
|
||||
logging.info(f"Using config: {settings.model_dump_json()}") |
|
||||
g = Github(settings.github_token.get_secret_value()) |
|
||||
repo = g.get_repo(settings.github_repository) |
|
||||
use_pr = next( |
|
||||
(pr for pr in repo.get_pulls() if pr.head.sha == settings.commit_sha), None |
|
||||
) |
|
||||
if not use_pr: |
|
||||
logging.error(f"No PR found for hash: {settings.commit_sha}") |
|
||||
sys.exit(0) |
|
||||
use_pr.as_issue().create_comment( |
|
||||
f"📝 Docs preview for commit {settings.commit_sha} at: {settings.deploy_url}" |
|
||||
) |
|
||||
logging.info("Finished") |
|
@ -0,0 +1,97 @@ |
|||||
|
import logging |
||||
|
import re |
||||
|
|
||||
|
from github import Github |
||||
|
from pydantic import SecretStr |
||||
|
from pydantic_settings import BaseSettings |
||||
|
|
||||
|
|
||||
|
class Settings(BaseSettings): |
||||
|
github_repository: str |
||||
|
github_token: SecretStr |
||||
|
deploy_url: str | None = None |
||||
|
commit_sha: str |
||||
|
run_id: int |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
logging.basicConfig(level=logging.INFO) |
||||
|
settings = Settings() |
||||
|
|
||||
|
logging.info(f"Using config: {settings.model_dump_json()}") |
||||
|
g = Github(settings.github_token.get_secret_value()) |
||||
|
repo = g.get_repo(settings.github_repository) |
||||
|
use_pr = next( |
||||
|
(pr for pr in repo.get_pulls() if pr.head.sha == settings.commit_sha), None |
||||
|
) |
||||
|
if not use_pr: |
||||
|
logging.error(f"No PR found for hash: {settings.commit_sha}") |
||||
|
return |
||||
|
commits = list(use_pr.get_commits()) |
||||
|
current_commit = [c for c in commits if c.sha == settings.commit_sha][0] |
||||
|
run_url = f"https://github.com/{settings.github_repository}/actions/runs/{settings.run_id}" |
||||
|
if not settings.deploy_url: |
||||
|
current_commit.create_status( |
||||
|
state="pending", |
||||
|
description="Deploy Docs", |
||||
|
context="deploy-docs", |
||||
|
target_url=run_url, |
||||
|
) |
||||
|
logging.info("No deploy URL available yet") |
||||
|
return |
||||
|
current_commit.create_status( |
||||
|
state="success", |
||||
|
description="Deploy Docs", |
||||
|
context="deploy-docs", |
||||
|
target_url=run_url, |
||||
|
) |
||||
|
|
||||
|
files = list(use_pr.get_files()) |
||||
|
docs_files = [f for f in files if f.filename.startswith("docs/")] |
||||
|
|
||||
|
lang_links: dict[str, list[str]] = {} |
||||
|
for f in docs_files: |
||||
|
match = re.match(r"docs/([^/]+)/docs/(.*)", f.filename) |
||||
|
assert match |
||||
|
lang = match.group(1) |
||||
|
path = match.group(2) |
||||
|
if path.endswith("index.md"): |
||||
|
path = path.replace("index.md", "") |
||||
|
else: |
||||
|
path = path.replace(".md", "/") |
||||
|
if lang == "en": |
||||
|
link = f"{settings.deploy_url}{path}" |
||||
|
else: |
||||
|
link = f"{settings.deploy_url}{lang}/{path}" |
||||
|
lang_links.setdefault(lang, []).append(link) |
||||
|
|
||||
|
links: list[str] = [] |
||||
|
en_links = lang_links.get("en", []) |
||||
|
en_links.sort() |
||||
|
links.extend(en_links) |
||||
|
|
||||
|
langs = list(lang_links.keys()) |
||||
|
langs.sort() |
||||
|
for lang in langs: |
||||
|
if lang == "en": |
||||
|
continue |
||||
|
current_lang_links = lang_links[lang] |
||||
|
current_lang_links.sort() |
||||
|
links.extend(current_lang_links) |
||||
|
|
||||
|
message = ( |
||||
|
f"📝 Docs preview for commit {settings.commit_sha} at: {settings.deploy_url}" |
||||
|
) |
||||
|
|
||||
|
if links: |
||||
|
message += "\n\n### Modified Pages\n\n" |
||||
|
message += "\n".join([f"* {link}" for link in links]) |
||||
|
|
||||
|
print(message) |
||||
|
use_pr.as_issue().create_comment(message) |
||||
|
|
||||
|
logging.info("Finished") |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
Loading…
Reference in new issue