PDF в веб-приложении
Пользователь нажимает «Скачать счёт», ваш сервис делает PDF через pdfapi и отдаёт его пользователю. Ключ при этом остаётся на сервере.
Запрос из браузера раскроет ключ любому пользователю.
Обработчик#
Библиотека возвращает PDF в памяти, поэтому сохранять его на диск не нужно — отдайте байты сразу в ответ. Заголовок Content-Disposition: inline откроет PDF во вкладке браузера, а attachment предложит скачать файл.
# FastAPI: один асинхронный клиент на всё приложение.
@asynccontextmanager
async def lifespan(app: FastAPI):
async with AsyncPdfApi() as client:
app.state.pdfapi = client
yield
app = FastAPI(lifespan=lifespan)
@app.get("/invoice/{number}")
async def invoice(number: int, request: Request) -> Response:
pdf = await request.app.state.pdfapi.html(render_invoice(number))
return Response(pdf.content, media_type="application/pdf",
headers={"Content-Disposition": f'inline; filename="invoice-{number}.pdf"'})Экранирование#
При наличии данных пользователя в HTML рекомендуется пропускать их через шаблоны с автоэкранированием или явное экранирование. Иначе пользователь вставит в свой счёт чужую разметку. Подробнее — в Безопасности.
Кеш#
Если документ не меняется (например, счёт за закрытый месяц), сохраните PDF у себя после первого запроса. Повторная генерация тратит страницы и время на обработку.