0.简介
-
根据在"使用docker-compose构建快速API uvicorn nginx"中创建的模板,使用Jinja2的Templates函数创建网页模板。
- 之前,我曾使用Flask等进行试用,我想知道是否可以以相同的方式分发Template和Static文件。
*续集:使用FastAPI uvicorn Nginx(SSL / HTTPS)显示网页
实践:添加网页功能
*前提:从"使用docker-compose构建FastAPI uvicorn nginx的状态"开始
1.添加软件包
-
Jinja2 用于模板功能,需要额外安装aiofiles ,以分发静态文件- 参考:官方文件
-
向
pyproject.toml 添加如下
pyproject.toml(其他)
1 2 3 4 | [tool.poetry.dependencies] # 以下の2つを追加 jinja2 = "*" aiofiles = "*" |
总计,例如:
pyproject.toml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [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.修改app 的内容?添加
- 基本上,只是参考官方例子
- 在这里,考虑创建多个页面或将这些功能与Rest-API函数等分离的可能性,并参考此内容以子应用程序的形式创建网页。
文件结构(app )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | $ 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 |
下查看详细信息
main.py
- 修改内容如下
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | """ app main """ import pathlib from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import RedirectResponse from .routers import subpage # pathlib.Pathを使って、staticディレクトリの絶対パスを取得 PATH_STATIC = str(pathlib.Path(__file__).resolve().parent / "static") def create_app(): """ create app - 少し複雑化してきたので関数化している """ _app = FastAPI() # routersモジュールのサブアプリ`subpage`をURL"/subpage/"以下にマウントする _app.include_router( subpage.router, prefix="/subpage", tags=["subpage"], responses={404: {"description": "not found"}}, ) # static # URL`/static"以下にstaticファイルをマウントする _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( # subpageのサブアプリで作ったWebページにリダイレクトさせている "/subpage", ) |
行数略有增加,但是我主要在做
-
添加
static -
子应用:挂载并重定向
subpage
routers/subpage.py
*
路由器/ subpage.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | """ test subpage """ import pathlib from fastapi import ( APIRouter, Request, ) from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse # templatesディレクトリの絶対パスを取得 PATH_TEMPLATES = str( pathlib.Path(__file__).resolve() \ .parent.parent / "templates" ) # Jinja2のobject生成 templates = Jinja2Templates(directory=PATH_TEMPLATES) # サブアプリ 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`ディレクトリにおける相対パス context={ # 変数をdict形式で渡すことが出来る "request": request, "title": title, } ) |
-
使用Jinja2的模板功能,在
templates 目录下指定文件,并将其作为HTML响应返回。 - 您可以传递Flask等参数。
templates 目录
layout.html
-
假定由多个html文件共同使用
-
extends 从另一个文件调用
-
layout.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <!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 }} {% else %} Template {% 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> |
-
读取
jQuery ,jQuery-UI ,Boostrap -
接收
title 作为参数 -
在单独的文件中分别填写
head 和contents 的内容。 -
我正在使用Jinja2的
url_for 从静态目录加载layout.css 进行测试
subpage/index.html
子页面/ index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | {% 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 %} |
-
layout.html 是extends ,并且通过从FastAPI端接收必要的参数,它成为完整的HTML。 -
从静态目录读取
test.css 和test.js 进行测试
static 目录
- 由于仅放置用于测试,几乎没有内容,因此省略了详细信息。
- 我将确认以后执行时可以正确读取内容
3.执行(第1部分)
执行以下
1 2 3 4 5 | # パッケージ追加、ソース修正?追加を行ったのでビルドし直す docker-compose build # サービスの起動 docker-compose up -d |
如果您在本地环境中运行,请查看http://本地主机
乍一看似乎可行,但是如果仔细观察,您将无法从HTML读取
1 2 3 4 5 6 7 8 9 | <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> |
-
在这种情况下,我希望
src 或href 的URL为http://localhost/<url> ,但它变为http://backend/<url> ,如↑所示。-
尝试在HTML文件上使用
url_for 时发生上述问题-
在FastAPI代码(
main.py ,routers/***.py 等)中使用url_for 没问题。
-
在FastAPI代码(
-
由于此区域存在代理问题,因此有必要修改
。
4.修改并执行设置(第2部分)
4-1。uvicorn
-
如果您阅读uvicorn Official的部署和设置部分,则似乎需要设置
--proxy-headers 选项。
最后,按如下所示更改Dockerfile中的
的启动选项
Dockerfile(更正)
1 2 | # 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
接下来,修改Nginx配置文件(
(请参阅部署uvicorn的Nginx示例)
1 2 3 4 5 6 7 | $ tree . ├── app ├── docker-compose.yml └── web └── conf.d └── app.conf |
-
如下修改↑中的
app.conf -
在
location / 中添加了项目:
-
conf.d / app.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | upstream backend { server app:8000; } server { listen 80; # server_name localhost; # index index.html index.htm; location / { # 以下の5項目を追加 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; |
到目前为止,如果您期望的URL被调用,就会感觉到
上正确完成了。
- ↑从static调用的文件的URL是正确的
概要
- 使用FastAPI uvicorn Nginx(docker-compose)的配置在Flask气氛下创建Web函数
-
当使用Template函数创建Web函数时,Flask更易于使用,而且我认为它很容易,因为文档很多。
- FastAPI专用于RestAPI功能,可能不擅长Web功能。但是,如果您可以掌握异步处理,则可能会提高性能。
-
我也进行了SSL转换(这很麻烦)
,所以我打算以后再写 del>- →续集:使用FastAPI uvicorn Nginx(SSL / HTTPS)显示网页
参考
- FastAPI官方文档
-
starlette官方文档
- FastAPI(显然)是starlette的扩展,因此您需要适当地检查starlette规范。
- uvicorn官方文档
- https://note.com/yusugomori/n/n9f2c0422dfcd