Python Falcon 简明教程
Python Falcon - Jinja2 Template
Falcon 库主要用于构建 API 和微服务。因此,默认情况下,Falcon 响应器返回 JSON 响应。但是,如果将内容类型更改为 falcon.MEDIA_HTML, ,则可以呈现 HTML 输出。
使用变量数据呈现 HTML 内容非常繁琐。为此,需要使用网络模板库。许多 Python Web 框架都捆绑了特定的模板库。但 Falcon 是一个极简主义微框架,不会捆绑任何模版库。
Jinja2 是许多 Python 框架使用的一个最流行的模板库。在本节中,我们将看到如何在 Falcon 应用中使用 inja2。jinja2 是一种快速且对设计人员友好的模板语言,易于配置和调试。它的沙盒环境便于阻止执行不可信代码、禁止潜在不安全数据,并防止跨站点脚本攻击(称为 XSS attacks )。
jinja2 的另一个非常强大的功能是 template inheritance ,其中您可以定义一个具有通用设计功能的基本模板,而子模板可以覆盖该基本模板。
首先,使用 PIP 实用程序在当前 Python 环境中安装 jinja2 。
pip3 install jinja2
Hello World Template
jinja2 模块定义了 Template 类。Template 对象是通过读取包含 HTML 脚本(带 .html 扩展名)的文件内容来获取的。通过调用这个 Template 对象的 render() 方法,可以向客户端浏览器呈现 HTML 响应。Response 对象的 content_type 属性必须设置为 falcon.MEDIA_HTML 。
让我们将以下 HTML 脚本另存为 hello.py 放置在应用文件夹中。
<html>
<body>
<h2>Hello World</h2>
</body>
</html>
Example
下面的资源类中的 on_get() 响应器读取这个文件并将其呈现为 HTML 响应。
import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
async def on_get(self, req, resp):
resp.status = falcon.HTTP_200
resp.content_type = 'text/html'
fp=open("hello.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render()
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == "__main__":
uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)
Template Variable
jinja2 是一个服务器端模板库。网页通过将 jinja2 模板语言的各种元素作为占位符放置到 HTML 脚本内的适当定界符中来构造为模板。模板引擎读取 HTML 脚本,在服务器上用上下文数据替换占位符,重新组装 HTML 并将其呈现给客户端。
Template.render() 函数有一个可选的上下文字典参数。这个字典的关键属性变成模板变量。这有助于呈现响应器在网页中传递的数据。
Example
在以下示例中,路由 /hello/nm 已使用路径参数 nm 注册了资源对象。 on_get() 响应器将其作为上下文传递给从网页获取的模板对象。
import uvicorn
import falcon
import falcon.asgi
from jinja2 import Template
class HelloResource:
async def on_get(self, req, resp, nm):
resp.status = falcon.HTTP_200
resp.content_type = 'text/html'
fp=open("hello.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render({'name':nm})
app = falcon.asgi.App()
hello = HelloResource()
app.add_route('/hello/{nm}', hello)
if __name__ == "__main__":
uvicorn.run("hello:app", host="0.0.0.0", port=8000, reload=True)
hello.html 在模板变量名称中读取路径参数。它充当 HTML 脚本中的占位符。它被放置在 {{ 和 }} 符号中,以便其值显示为 HTML 响应。
<html>
<body>
<h2>Hello {{ name }}</h2>
</body>
</html>
Loop in jinja2 Template
如果响应器传递任何 Python 可迭代对象,例如列表、元组或字典,则可以使用其循环构造语法在 jinja2 模板内部遍历其元素。
{% for item in collection %}
HTML block
{% endfor %}
在以下示例中, on_get() 响应器向模板 list.html 发送 students 对象,该对象是 dict 对象的列表。它会遍历数据并将其呈现为 HTML 表格。
import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
{"id": 1, "name": "Ravi", "percent": 75.50},
{"id": 2, "name": "Mona", "percent": 80.00},
{"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
def on_get(self, req, resp):
resp.status = falcon.HTTP_OK
resp.content_type = falcon.MEDIA_HTML
fp=open("list.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render({'students':students})
list.html 是一个 jinja2 模板。它接收 students 对象作为字典对象列表,并将每个键的值放在表的 <td>..<.td> 元素内部。
<html>
<body>
<table border=1>
<thead> <tr>
<th>Student ID</th> <th>Student Name</th>
<th>percentage</th>
<th>Actions</th>
</tr> </thead>
<tbody>
{% for Student in students %}
<tr> <td>{{ Student.id }}</td> <td>{{ Student.name }}</td>
<td>{{ Student.percent }}</td>
<td>
<a href="#">Edit</a>
<a href="#">Delete</a>
</td> </tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
访问浏览器地址栏中的 /students 路由。学生的列表将在浏览器中呈现。
HTML Form Template
在此部分,我们将看到 Falcon 如何从 HTML 表单读取数据。让我们将以下 HTML 脚本保存为 myform.html。我们将使用它来获取 Template 对象并呈现它。
<html>
<body>
<form method="POST" action="http://localhost:8000/students">
<p>Student Id: <input type="text" name="id"/> </p>
<p>student Name: <input type="text" name="name"/> </p>
<p>Percentage: <input type="text" name="percent"/> </p>
<p><input type="submit"> </p>
</body>
</html>
Falcon App 对象在 Hello.py 文件中声明,其中还将资源类映射到 /adddnew 路由。 on_get() 响应者读取 myform.html 并对它进行呈现。HTML 表单将会显示出来。通过 POST 方法将表单提交到 /students 路由。
要能够读取表单数据,必须将 auto_parse_form_urlencoded 类的 falcon.RequestOptions 属性设置为 True。
app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True
此处,我们还从 student.py 中导入 StudentResource 类。 on_get() 响应器呈现学生列表。
当用户填写并提交表单时,将调用 on_post() 响应器。此方法收集表单数据到 req.params 属性中,它只不过是一个表单元素及其值的字典。然后附加 students 字典。
def on_post(self, req, resp):
student=req.params
students.append(student)
hello.py 的完整代码如下 −
import falcon
import json
from waitress import serve
from jinja2 import Template
from student import StudentResource
class MyResource:
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
resp.content_type = 'text/html'
fp=open("myform.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render()
app = falcon.App()
app.req_options.auto_parse_form_urlencoded = True
form = MyResource()
app.add_route('/addnew', form)
app.add_route("/students", StudentResource())
if __name__ == '__main__':
serve(app, host='0.0.0.0', port=8000)
具有 StudentResource 类和 on_get() 及 on_post() 响应器的 student.py 如下 −
import falcon
import json
from waitress import serve
from jinja2 import Template
students = [
{"id": 1, "name": "Ravi", "percent": 75.50},
{"id": 2, "name": "Mona", "percent": 80.00},
{"id": 3, "name": "Mathews", "percent": 65.25},
]
class StudentResource:
def on_get(self, req, resp):
resp.status = falcon.HTTP_OK
resp.content_type = falcon.MEDIA_HTML
fp=open("list.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render({'students':students})
def on_post(self, req, resp):
student = req.params
students.append(student)
resp.text = "Student added successfully."
resp.status = falcon.HTTP_OK
resp.content_type = falcon.MEDIA_JSON
从命令行运行 hello.py 。通过输入 http://locLhost:8000/addnew 在浏览器中打开 HTML 表单。
将附加 students 数据库字典。访问 /students 路由。你会发现附加了新行。
Multipart Forms
为了让用户从本地文件系统选择文件,必须将 HTML 表单的 enctype 属性设置为 multipart/form-data。Falcon 使用 MultipartFormHandler 处理 multipart/form-data 媒体类型,允许其迭代表单中的主体部分。
BodyPart 类具有以下属性 −
-
stream − 仅适用于当前主体部分的流包装器
-
data − 主体部分内容字节
-
content_type 如果未指定,将默认为 text/plain,根据 RFC
-
text − 将当前主体部分解码为文本字符串(仅当它为类型 text/plain 时提供,否则不提供)
-
media − 由媒体处理程序以与 req.media 相同的方式自动解析
-
name, filename − 来自 Content-Disposition 标头的相关部分
-
secure_filename − 已清理的文件名,可以在服务器文件系统安全地使用。
以下 HTML 脚本 ( index.html ) 是一个多部分表单。
<html>
<body>
<form action="http://localhost:8000/hello" method="POST" enctype="multipart/form-data">
<h3>Enter User name</h3>
<p><input type='text' name='name'/></p>
<h3>Enter address</h3>
<p><input type='text' name='addr'/></p>
<p><input type="file" name="file" /></p>
<p><input type='submit' value='submit'/></p>
</form>
</body>
</html>
此表格由下方代码中的 on_get() 响应器渲染。将表格数据提交给 on_post() 方法,该方法将迭代部分并发送表格数据的 JSON 响应。
import waitress
import falcon
import json
from jinja2 import Template
class HelloResource:
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
resp.content_type = 'text/html'
fp=open("index.html","r")
tempobj=Template(fp.read())
resp.body=tempobj.render()
def on_post(self, req, resp):
result=[]
for part in req.media:
data={"name" :part.name,
"content type":part.content_type,
"value":part.text, "file":part.filename}
result.append(data)
resp.text = json.dumps(result)
resp.status = falcon.HTTP_OK
resp.content_type = falcon.MEDIA_JSON
app = falcon.App()
hello = HelloResource()
app.add_route('/hello', hello)
if __name__ == '__main__':
waitress.serve(app, host='0.0.0.0', port=8000)
运行以上程序并访问 http://localhost:8000/hello 链接,如以下所示,以渲染表格 -
当在填写数据后提交表格时,将在浏览器中显示 JSON 响应,如下所示:
[
{
"name": "name",
"content type": "text/plain",
"value": "SuyashKumar Khanna",
"file": null
},
{
"name": "addr",
"content type": "text/plain",
"value": "New Delhi",
"file": null
},
{
"name": "file",
"content type": "image/png",
"value": null,
"file": "hello.png"
}
]