[PYTHON] Afficher une page Web avec FastAPI + uvicorn + Nginx (fonction Modèles de Jinja2)

0. Introduction

Pratique: fonction de page Web ajoutée

1. Ajouter un package

--Jinja2 pour la fonction Template nécessite une installation supplémentaire de ʻaiofilespour fournir des fichiers statiques --Référence: [Document officiel](https://fastapi.tiangolo.com/advanced/templates/#install-dependencies) --Ajout de ce qui suit àpyproject.toml`

pyproject.toml(Ajouts)


[tool.poetry.dependencies]
#Ajout des deux suivants
jinja2 = "*"
aiofiles = "*"

Au total, par exemple:

pyproject.toml


[tool.poetry]
name = "test_fastapi_app"
version = "0.1.0"
description = "just for test"
authors = ["Your Name <[email protected]>"]

[tool.poetry.dependencies]
python = "^3.8"
uvicorn = "*"
fastapi = "*"
jinja2 = "*"
aiofiles = "*"

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

2. Modifier / ajouter le contenu de ʻapp`

Structure du fichier (ʻapp`)

$ tree
.
├── app
│   ├── Dockerfile
│   ├── app
│   │   ├── __init__.py
│   │   ├── main.py
│   │   ├── routers
│   │   │   ├── __init__.py
│   │   │   └── subpage.py
│   │   ├── static
│   │   │   ├── layout.css
│   │   │   └── subpage
│   │   │       ├── test.css
│   │   │       └── test.js
│   │   └── templates
│   │       ├── layout.html
│   │       └── subpage
│   │           └── index.html
│   ├── poetry.lock
│   └── pyproject.toml
├── docker-compose.yml
└── web

Puisque les fichiers dans ʻapp / app` ont été modifiés / ajoutés, nous regarderons les détails ci-dessous.

main.py

--Correction du contenu comme suit

main.py


"""                         
app main                      
"""                        
                      
import pathlib
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse

from .routers import subpage

# pathlib.Obtenez le chemin absolu d'un répertoire statique à l'aide de Path
PATH_STATIC = str(pathlib.Path(__file__).resolve().parent / "static")


def create_app():
    """
    create app

    -Ça devient un peu compliqué, donc c'est fonctionnalisé
    """
    _app = FastAPI()

    #Module sous-application des routeurs`subpage`URL"/subpage/"Monter ci-dessous
    _app.include_router(
        subpage.router,
        prefix="/subpage",
        tags=["subpage"],
        responses={404: {"description": "not found"}},
    )

    # static
    # URL`/static"Montez le fichier statique ci-dessous
    _app.mount(
        "/static",
        StaticFiles(directory=PATH_STATIC, html=False),
        name="static",
    )

    return _app


app = create_app()


@app.get('/')
async def redirect_subpage():
    """redirect webpage"""
    return RedirectResponse( #Redirection vers une page Web créée avec une sous-application de sous-page
        "/subpage",
    )

Le nombre de lignes a un peu augmenté, mais je fais principalement

routers/subpage.py

routers/subpage.py


"""
test subpage
"""

import pathlib

from fastapi import (
    APIRouter,
    Request,
)
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse

#Obtenez le chemin absolu du répertoire des modèles
PATH_TEMPLATES = str(
    pathlib.Path(__file__).resolve() \
        .parent.parent / "templates"
)
#Génération d'objets Jinja2
templates = Jinja2Templates(directory=PATH_TEMPLATES)


#Sous application
router = APIRouter()


@router.get("/", response_class=HTMLResponse)
async def site_root(
    request: Request,
):
    """test subpage"""
    title = "test subpage"
    return templates.TemplateResponse(
        "subpage/index.html",   # `templates`Chemin relatif dans le répertoire
        context={   #Les variables peuvent être transmises au format dict
            "request": request,
            "title": title,
        }
    )

Répertoire templates

layout.html

layout.html


<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        {% if title %}
            <title>{{ title }}</title>
        {% else %}
            <title>Template</title>
        {% endif %}

        <!-- jQuery & Bootstrap4 -->
        <script
            src="https://code.jquery.com/jquery-3.4.1.min.js"
            integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
            crossorigin="anonymous"></script>
        <link
            rel="stylesheet"
            href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
            integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
            crossorigin="anonymous">

        <script
            src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"
            integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
            crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
            integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
            crossorigin="anonymous"></script>

        <!-- jQuery UI -->
        <script
            src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
            integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
            crossorigin="anonymous"></script>
        <link
            rel="stylesheet"
            type="text/css"
            href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.min.css">

        <!-- CUSTOM STYLE -->
        <link rel="stylesheet" type="text/css" href="{{url_for('static', path='/layout.css')}}">
        {% block head %}{% endblock %}

    </head>

    <body>
        {% block content %}{% endblock %}
    </body>

</html>

--Chargement de jQuery, jQuery-UI, Bootstrap --title est reçu comme paramètre

subpage/index.html

subpage/index.html


{% extends "layout.html" %}

{% block head %}
<link
    rel="stylesheet"
    type="text/css"
    href="{{ url_for('static', path='/subpage/test.css')  }}">
<script
    type="text/javascript"
    src="{{ url_for('static', path='subpage/test.js') }}"></script>
{% endblock %}


{% block content %}
<h2>Test Subpage</h2>

<br>

<h3>
    Hello, World.
</h3>

{% endblock %}

Répertoire static

3. Exécution (1)

Faites ce qui suit

#Package ajouté, source modifiée / ajoutée, donc reconstruire
docker-compose build

#Démarrage du service
docker-compose up -d

Si vous exécutez dans un environnement local, jetez un œil à http: // localhost

スクリーンショット 2020-08-16 00-28-56.png

スクリーンショット 2020-08-16 00-34-01.png

À première vue, cela semble fonctionner, mais si vous regardez de près, vous ne pouvez pas lire le fichier static à partir du HTML:

<link rel="stylesheet" type="text/css" href="http://backend/static/layout.css">
        
<link
    rel="stylesheet"
    type="text/css"
    href="http://backend/static/subpage/test.css">
<script
    type="text/javascript"
    src="http://backend/static/subpage/test.js"></script>

Puisqu'il y a un problème de proxy dans ce domaine, il est nécessaire de modifier l'option de démarrage de ʻuvicornet le paramètre denginx` pour y faire face.

4. Modifier et exécuter les paramètres (partie 2)

4-1. uvicorn

--Si vous faites de votre mieux pour lire les parties déploiement et setting de l'officiel uvicorn, `- Il semble que l'option des en-têtes de proxy doit être définie

En conclusion, changez le CMD du Dockerfile comme suit pour corriger l'option de démarrage de ʻuvicorn`

Dockerfile (correction)


# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--proxy-headers", "--forwarded-allow-ips", "*"]

4-2. Nginx

Ensuite, modifiez le fichier de configuration Nginx (web / conf.d / app.conf) (Voir le [déploiement] d'uvicorn (https://www.uvicorn.org/deployment/) pour un exemple de Nginx)

$ tree    
.
├── app
├── docker-compose.yml
└── web
    └── conf.d
        └── app.conf

-Modifier ʻapp.conf` dans ↑ comme suit

conf:conf.d/app.conf


upstream backend {
    server app:8000;
}

server {
    listen 80;
    # server_name  localhost;
    # index index.html index.htm;

    location / {
        #Ajout des 5 éléments suivants
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
        proxy_buffering off;

        proxy_pass http://backend;
    }

    # log
    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
}

# server_tokens off;

Si vous faites jusqu'ici, l'URL que vous attendiez sera appelée correctement comme http: // localhost / <url> même si vous avez fait ʻurl_for` sur le fichier HTML.

image.png

Résumé

--FastAPI + uvicorn + Nginx (docker-compose) configuration pour créer une fonction web avec l'atmosphère de Flask --Flask est plus facile à utiliser lors de la création de fonctions Web à l'aide de la fonction Template, etc., et j'ai trouvé que c'était plus facile car il y a beaucoup de documents. --FastAPI est spécialisé pour les fonctions RestAPI et peut ne pas être bon pour les fonctions Web. Cependant, si vous pouvez maîtriser le traitement asynchrone, il peut y avoir un potentiel de performances. --J'ai également fait une conversion SSL (et c'était assez gênant) ~~, donc je prévois d'écrire plus tard ~~

référence

Recommended Posts

Afficher une page Web avec FastAPI + uvicorn + Nginx (fonction Modèles de Jinja2)
Afficher la page Web avec FastAPI + uvicorn + Nginx (conversion SSL / HTTPS)
Construisez FastAPI + uvicorn + nginx avec docker-compose
Extraire des données d'une page Web avec Python
Lancer une application Web Python sur Nginx + Gunicorn avec Docker
Obtenez la page Web Python, encodez et affichez les caractères
Afficher Google Maps sur la page Web (Spring Boot + Thymeleaf)
[python, ruby] sélénium-Obtenez le contenu d'une page Web avec le pilote Web
Pratique de développement d'applications Web: Créez une page de création d'équipe avec Django! (Page de création de décalage)
[FastAPI] Premiers pas avec FastAPI, un framework Web ASGI créé par Python