FastAPI: как совместить хостинг приложения и статических файлов?

Вот я пишу "Hello world" с использованием FastAPI:

файл main.py, в котором написано

import uvicorn
from fastapi import FastAPI

app = FastAPI()

if __name__ == '__main__':
    uvicorn.run("main:app", host='0.0.0.0', port=80, reload=True)

@app.get("/api")
async def root():
    return {"message": "Hello World"}

Перед этим я установил зависимости

pip install fastapi
pip install uvicorn

Всё хорошо, я запускаю приложение python main.py, и при заходе в браузер по url http://127.0.0.1/api получаю заветный json {"message":"Hello World"}

Затем я добавляю хостинг статических файлов: создаю директорию static, кладу туда index.html с какой нибудь строчкой, и меняю свой python файл так:

import uvicorn
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()
app.mount('/', StaticFiles(directory='static',html=True))

if __name__ == '__main__':
    uvicorn.run("main:app", host='0.0.0.0', port=80, reload=True)

@app.get("/api")
async def root():
    return {"message": "Hello World"}

При этом у меня работает хостинг статического файла - по url http://127.0.0.1/ открывается index.html, но... запрос по прежнему url http://127.0.0.1/api работать перестаёт

Причем, если я поменяю строчку, задающую роутинг для статических файлов на

app.mount('/static/', StaticFiles(directory='static',html=True))

то оба запроса, и по http://127.0.0.1/static/, и по http://127.0.0.1/api - отработают правильно.

Кажется, что есть конфликт роутингов: роутинг по более короткому пути / "перебивает" роутинг по пути /api

Как бы мне сделать, чтобы и index.html грузился, причем по "короткому" url http://127.0.0.1/, и мой api по пути http://127.0.0.1/api тоже работал?

Поясню: я хочу на основе FastAPI сделать такой "сайт для бедных": чтобы с него грузилась страница с JS-ом, который после загрузки начинает обращаться к api

Спасибо!


Ответы (1 шт):

Автор решения: S.H.

Спасибо большое insolor и CrazyElf!

Вы указали что искать, и готовый ответ нашелся вот здесь: https://stackoverflow.com/questions/60716529/download-file-using-fastapi

Таким образом, вот первый работающий вариант того, что я хотел:

import uvicorn
from fastapi import FastAPI
from starlette.responses import FileResponse

app = FastAPI()

@app.get("/")
async def read_index():
    return FileResponse('index.html')

@app.get("/api")
async def root():
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run("main:app", host='0.0.0.0', port=80, reload=True)

а вот - второй: (лично мне монтирование путей через mount нравится больше, чем через "магические атрибуты")

import uvicorn
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

@app.get('/api')
def api_app():
    return {"message": "Hello World"}

app.mount('/api', app)
app.mount('/', StaticFiles(directory='static',html=True))

if __name__ == '__main__':
    uvicorn.run("main:app", host='0.0.0.0', port=80, reload=True)

Запуск: python main.py

Затем надо в браузере зайти на http://localhost/

→ Ссылка