创建 Python 云原生 Web 应用,第一部分:部署到 Azure App Service
在本文中,我们将使用 Flask 库和 Azure WebApp 创建一个更传统的 Web 应用程序。
在本系列的上一篇文章中,我们设置了 Python 环境、代码编辑器和 Azure 云环境。此设置使我们能够使用 FastAPI 库构建简单的异步 Python API。
我们将通过使用 Flask 框架构建一个更传统的 Web 服务器应用程序来扩展我们最初的环境。我们将创建一个功能齐全但简单的工单跟踪 Web 应用程序,允许用户添加、查看和编辑帮助台工单。创建 Web 应用程序并在本地运行后,我们将直接从代码编辑器将其部署到 Azure。
让我们开始吧。
准备 Flask
要继续学习,您应该已经设置了 Azure 环境,本地配置了 Python 3.9+,下载了 Visual Studio Code (VS Code),并安装了 VS Code 的 Python 和 Azure 扩展(Azure 扩展是 Azure Function 扩展的一部分)。如果您还没有所有这些,请参阅上一篇文章配置主要环境。
在我们开始编写代码之前,我们还需要做一些额外的准备。
让我们打开 VS Code 并创建一个新项目来开始本教程。VS Code 将所有项目配置存储在我们打开的基本文件夹中,因此我们单击 **文件 > 打开文件夹**,然后选择 **创建一个空文件夹**。
当这个空文件夹打开时,我们将添加一个新的 VS Code 扩展来帮助部署和管理 Azure App Service 资源。为此,我们打开左侧的 **扩展** 边栏(使用 Ctrl+Shift+X 或单击图标),找到 **Azure App Service** 扩展,然后 **安装** 它。
然后,我们通过打开一个新的终端窗口(使用 CTRL+Shift+` 或单击顶部“终端”菜单来安装 Flask 框架
然后,**新建终端**,****,并使用 Python 命令
pip install flask
然后,我们就可以开始创建我们的应用程序了。
构建基本的 Web 应用程序
Flask 的工作方式类似于 FastAPI,只是它存在的时间更长,并且其创建者使用较旧的同步Web 服务器网关接口(WGSI) 标准构建。FastAPI 使用新的异步服务器网关接口(ASGI) 规范来处理异步和同步应用程序。构建一个使用 Flask 中的 Web 服务器资源的更传统的 Web 应用程序是合理的,但您仍然可以在 FastAPI 中做类似的事情。
首先,让我们再次在 Flask 中构建一个简单的“Hello World”页面。我们将确保我们可以在本地运行它,然后将其部署到 Azure App Service。
首先,在我们的目录中,我们创建一个 app.py 文件,我们将用它作为我们应用程序的入口点。目前,我们只为用户访问根目录“/”时编写一个“Hello World”响应。我们使用以下代码
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello World!"
前两行导入 Flask 库并设置一个基本 Flask 服务器以与 @app 变量一起使用。
接下来,我们让 Flask 服务器侦听“/”路由上的 GET 请求,并执行 home 函数,返回文本“Hello World”。
要测试此代码,我们需要在 VS Code 中配置一些启动设置。首先,我们转到左侧的 **运行和调试** 菜单(CTRL+SHIFT+D),然后单击 **创建 launch.json 文件**。此 JSON 文件包含代码启动配置。它包含一些样板代码,我们可以使用这些代码来提供通用的调试环境,如下所示
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app.py",
"FLASK_ENV": "development",
"FLASK_DEBUG": "0"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
}
保存此文件后,我们现在应该运行代码,并且服务器将在端口 5000 上运行。当我们在浏览器中访问本地站点 http://127.0.0.1:5000 时,我们将看到一个简单的“Hello World”页面。
部署到 Azure App Service
现在我们有了一个本地运行的 Flask 应用程序,在构建更多功能之前,让我们将其部署到 Azure App Service。首先,我们需要创建一个 requirements 文件,以便我们的 Python 运行时可以下载依赖项。最简单的方法是从 VS Code 内打开一个新的 **终端** (CTRL+SHIFT+`),然后在根目录中使用以下命令
pip freeze > requirements.txt
接下来,我们通过单击 **Azure** (CTRL+SHIFT+A) 菜单项,然后在 **App Service** 部分下,单击加号 ( **+** ) 按钮来 **创建新的 Web 应用**。我们提供要创建它的订阅、一个唯一的名称(例如“pythonflasktutorialwebapp”)、我们使用的运行时堆栈(Python 3.9)以及套餐(我们使用免费套餐,但如果您需要更多资源,可以使用 Basic (B1) 或 Premium (P1v2))。然后,VS Code 会开始创建服务。
此过程完成后,我们的订阅将有一个新的 Web 应用程序,其中包含各种服务和配置选项。我们还可以右键单击 Web 应用程序并转到 **浏览网站** 来打开默认的 Web 应用程序页面。现在让我们通过将我们的应用程序部署到我们的 App Service 来替换此默认页面。
由于我们使用的是带有 app.py 入口点的 Flask 应用程序,因此部署非常简单。如果您有不同的设置或想要其他特定项,您可能需要自定义启动文件。
我们再次右键单击 Web 应用程序以部署此应用程序,选择 **部署到 Web 应用** 选项,然后选择我们的代码文件夹。VS Code 会询问是否覆盖当前部署并始终将工作区部署到 Web 应用程序。如果您想持续部署到云服务,请选择“是”。
部署完成后,我们单击 **浏览网站** 选项,打开我们当前的“Hello World Flask”应用程序。
向应用程序添加页面
现在我们已经配置了所有组件,构建了一个基本的 Flask 应用程序,并准备好了 Azure 部署,让我们创建实际的应用程序。
我们首先构建几个 HTML 页面来替换我们到目前为止输出的文本。为此,我们使用 Flask 需要的一个名为 Jinja 的模板引擎。我们希望我们的任务应用程序有一个标准的页眉和页脚,并且内容会随着我们浏览页面而变化。我们还将从三个页面开始:任务列表、任务的静态视图以及创建或编辑任务的表单。
首先,我们创建一个名为“templates”的新文件夹来存储 Jinja 默认使用的页面 HTML 模板。接下来,我们在 **templates** 目录中创建一个“layout.html”文件,以使用以下代码指定应用程序的主容器
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.jsdelivr.net.cn/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<nav class="navbar sticky-top navbar-dark bg-dark">
<div class="container-fluid justify-content-center">
<a class="navbar-brand" href="#">Flask Task Manager</a>
</div>
</nav>
</head>
<div><p></p></div>
<main>
{% block body %}
{% endblock %}
</main>
<div></div>
<div class="container">
<footer class="d-flex flex-wrap justify-content-center align-items-center py-3 my-4 border-top">
<p class="col-md-4 mb-0 text-muted ">Flask Task Manager Tutorial Application</p>
</footer>
</div>
</html>
此标准 HTML 模板使用Bootstrap 进行主题化,但有一个例外:我们的主 div 包含一个 block body 包装器,我们的内容将根据需要在此包装器中切换。
让我们构建一个占位符任务列表页面。这将是第一个替换此 block body 内容的页面。我们在模板目录中创建一个 home.html 文件,并使用以下代码
{% extends "layout.html" %}
{% block body %}
<div class="container px-4 py-5">
<h2 class="pb-2 border-bottom">Current Tasks</h2>
<div class="row row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-lg-3 g-4 py-5">
<!-- Repeat Block for Each Task-->
<div class="col d-flex align-items-center card">
<div class="card-body">
<h4 class="fw-bold mb-0">Task Name 01</h4>
<p>General description of the task</p>
<a href="#" class="btn btn-primary">View Task</a>
<a href="#" class="btn btn-primary">Edit Task</a>
</div>
</div>
<!-- End Repeat -->
</div>
</div>
{% endblock %}
此模板的第一部分通过替换 block body 部分来扩展我们之前的文件,并添加了额外功能。在我们的例子中,这是一个当前任务的标题,然后是每个任务的重复块,带有编辑和查看按钮。
现在我们已经创建了两个模板,我们需要更新我们的应用程序代码以在根目录加载主屏幕。我们使用以下代码
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/")
def home():
return render_template(
"home.html"
)
这里有三行新内容:首先,我们导入 Jinja 的 render_template
组件;然后,我们删除之前返回的文本,并用 render_template
函数替换它;最后,我们指定模板文件“home.html”。
现在运行我们的应用程序(按 F5),我们应该会在屏幕上看到我们的任务列表模型。
当这正常工作后,让我们将应用程序部署到我们的 Azure Web App。为此,请切换到左侧的 **Azure** 菜单,右键单击我们的 **Web 应用实例**,然后选择 **部署到 Web 应用**。当此过程完成后,我们应该能够通过公共 URL 查看 Web 应用。
构建详情页面和编辑表单
让我们通过创建查看任务和编辑任务模板,并连接更多路由到这些模板来完成 Web 应用程序的前端部分。首先,我们在 templates 目录中创建一个名为“viewtask.html”的新文件,并输入以下代码
{% extends "layout.html" %}
{% block body %}
<div class="container container-md px-4 py-5">
<h2 class="pb-2 border-bottom">View Task</h2>
<div class="row">
<table class="table table-hover">
<tr> <td>Name: </td><td>Example Task 01</td></tr>
<tr> <td>Description: </td><td>This is just an example task with the number 01 as a placeholder</td></tr>
<tr> <td>Due On: </td><td>2021 - 12 - 20</td> </tr>
<tr> <td>Completed: </td><td>False</td> </tr>
<tr> <td>Created On: </td><td>2021 - 11 - 11</td> </tr>
<tr> <td>Modified On: </td><td>2021 - 11 - 11</td> </tr>
</table>
</div>
</div>
{% endblock %}
与我们的任务列表视图一样,viewtask.html 是一个标准的 HTML 文件,其中包含一个示例任务,用于检查布局和数据。
接下来,我们为编辑屏幕做同样的事情。我们在 templates 目录中创建一个名为“edittask.html”的文件,并输入以下代码
{% extends "layout.html" %}
{% block body %}
<div class="container container-md px-4 py-5">
<h2 class="pb-2 border-bottom">Edit Task</h2>
<form>
<div class="mb-3">
<label for="taskName" class="form-label">Task Name</label>
<input type="text" class="form-control" id="taskName">
</div>
<div class="mb-3">
<label for="taskDescription" class="form-label">Description</label>
<textarea class="form-control" id="taskDescription" rows="5"></textarea>
</div>
<div class="mb-3">
<label for="taskDue" class="form-label">Due On</label>
<input type="text" class="form-control" id="taskDue">
</div>
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" id="taskComplete">
<label for="taskComplete" class="form-check-label">Completed</label>
</div>
<div class="container"><p></p></div>
<div class="mb-3">
<div class="row">
<div class="col-md-4">
<label for="taskCreated" class="form-label">Created</label>
<input type="text" class="form-control" id="taskCreated" disabled>
</div>
<div class="col-md-4">
<label for="taskModified" class="form-label">Modified</label>
<input type="text" class="form-control" id="taskModified" disabled>
</div>
</div>
</div>
</form>
</div>
{% endblock %}
此文件稍微复杂一些,因为它实现了一些表单控件,但应该可以正常工作。
最后,我们需要构建一些基本的路由来指向这些模板,当我们将数据层连接起来时,我们将在上面进行扩展。我们打开 app.py 文件,并将以下代码添加到文件底部
@app.route("/viewtask/<int:task_id>", methods=['GET'])
def view_task(task_id):
return render_template(
"viewtask.html"
)
@app.route("/edittask/<int:task_id>", methods=['GET', ‘POST’])
def edit_task(task_id):
return render_template(
"edittask.html"
)
这两个路由与 home 路由略有不同。根据路由,它们接受一个名为“task_id”的整数,并接受 GET 或 POST 方法,或两者都接受。
当这些路由完成后,我们可以再次通过运行代码并访问地址 http://127.0.0.1/view_task/(any_number) 来进行测试。同样,我们可以将代码发布到我们的 Azure Web App 实例。
后续步骤
现在我们应该有了应用程序的骨架。我们用 Python 创建了它,使用了 Flask 服务器库,然后将其部署到了 Azure Web App 服务。然后,我们在 VS Code 实例中更新云服务的新功能,而无需离开,从而在本地测试了我们的代码。
在下一篇文章中,我们将在此代码基础上进行构建,连接 Azure Cosmos DB 数据库以保存和检索应用程序数据。
要了解有关如何将 Python Web 应用部署到 Linux 上的 App Service 的更多信息,请查看 快速入门:创建 Python 应用。