Quiero servir mi interfaz React usando FastAPI. El objetivo es 0 dependencia de Javascript para el usuario. El usuario puede simplemente descargar el código de Python, iniciar el servidor y ver el sitio web en localhost.
Mi estructura de carpetas es:
- my-fullstack-app - frontend/ - build/ - public/ - ... - package.json - backend/ - main.py - static/ npm run build para generar la carpeta frontend/build/ que contiene:
build/ ├── asset-manifest.json ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── robots.txt └── static ├── css │ ├── main.073c9b0a.css │ └── main.073c9b0a.css.map ├── js │ ├── 787.cda612ba.chunk.js │ ├── 787.cda612ba.chunk.js.map │ ├── main.af955102.js │ ├── main.af955102.js.LICENSE.txt │ └── main.af955102.js.map └── media └── logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg Copié el contenido de la carpeta frontend/build/ dentro de backend/static/ .
Ahora, quiero servir esta carpeta backend/static/ a través de FastAPI en lugar de ejecutar otro servidor.
En main.py de mi main.py tengo:
from fastapi import FastAPI from fastapi.staticfiles import StaticFiles app = FastAPI() app.mount("/", StaticFiles(directory="static/"), name="static") Luego inicio el servidor usando - uvicorn main:app --reload .
Pero no funciona.
Cuando abro http://127.0.0.1:8000/ en el navegador, el resultado es un archivo JSON que dice {"detail":"Not Found"} y la consola tiene Content Security Policy: The page's settings blocked the loading of a resource at http://127.0.0.1:8000/favicon.ico ("default-src"). .
¿Cómo hago para que esto funcione? He visto ejemplos de funcionalidades similares con React y Express .
La respuesta mencionada por @flakes es exactamente lo que estaba buscando. Échale un vistazo para más detalles.
Por completitud,
Sin indicador html=True .
app.mount("/", StaticFiles(directory="static/"), name="static") Navegue a http://127.0.0.1:8000/index.html para ver el archivo html.
Con html=True .
app.mount("/", StaticFiles(directory="static/", html=True), name="static") Navegue a http://127.0.0.1:8000/ para ver el archivo html.
Lo que puede hacer es hacer que index.html sirva todas las rutas, excepto las específicas de API, luego cargue todos los activos estáticos. Mi enfoque se ve así:
from fastapi import FastAPI, Request from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from fastapi.exception_handlers import http_exception_handler from starlette.exceptions import HTTPException as StarletteHTTPException def SPA(app: FastAPI, build_dir: Union[Path, str]) -> FastAPI: # Serves a React application in the root directory @app.exception_handler(StarletteHTTPException) async def _spa_server(req: Request, exc: StarletteHTTPException): if exc.status_code == 404: return FileResponse(f'{build_dir}/index.html', media_type='text/html') else: return await http_exception_handler(req, exc) if isinstance(build_dir, str): build_dir = Path(build_dir) app.mount( '/static/', StaticFiles(directory=build_dir / 'static'), name='React app static files', )Luego, en su archivo de punto de entrada
app: FastAPI = SPA(FastAPI(title='PROJECT_NAME', './build')