Flask 是一个 Python 微型 Web 框架,用于运行包括 Pinterest、Twilio 和 LinkedIn 在内的主要网站。本章将解释并展示 Flask 为前后端 Web 开发提供的各种功能。
1: 文件和模板
与其在返回语句中输入 HTML 标记,不如使用 render_template() 函数:
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route("/about")
def about():
return render_template("about-us.html")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
这将使用我们的模板文件 about-us.html。为了确保应用程序能够找到该文件,我们必须按照以下格式组织目录结构:
- application.py
/templates
- about-us.html
- login-form.html
/static
/styles
- about-style.css
- login-style.css
/scripts
- about-script.js
- login-script.js
最重要的是,HTML 中对这些文件的引用必须如下所示:
这将指示应用程序在 static 文件夹下的 styles 文件夹中查找 about-style.css。所有对图像、样式、脚本或文件的引用都适用相同的路径格式。
2: 基础
以下是一个基本服务器的示例:
# 导入 Flask 类
from flask import Flask
# 创建一个应用并检查是否为主程序或导入
app = Flask(__name__)
# 指定触发 hello_world() 的 URL
@app.route('/')
# 在索引路由上运行的函数
def hello_world():
# 返回要显示的文本
return "Hello World!"
# 如果此脚本不是导入的
if __name__ == "__main__":
# 运行应用直到停止
app.run()
运行此脚本(安装了所有正确依赖项的情况下)将启动一个本地服务器。主机是 127.0.0.1,通常称为本地主机。此服务器默认运行在端口 5000 上。要访问你的 Web 服务器,请打开一个 Web 浏览器并输入 URL localhost:5000 或 127.0.0.1:5000(两者没有区别)。目前,只有你的计算机可以访问 Web 服务器。
app.run() 有三个参数:host、port 和 debug。主机默认为 127.0.0.1,但将其设置为 0.0.0.0 将使你的 Web 服务器可以通过网络中的任何设备使用你的私有 IP 地址访问。端口默认为 5000,但如果将参数设置为端口 80,用户将不需要指定端口号,因为浏览器默认使用端口 80。至于调试选项,在开发过程中(永远不要在生产环境中)将此参数设置为 True 是有帮助的,因为当对 Flask 项目进行更改时,服务器将重新启动。
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80, debug=True)
3: 路由 URL
在 Flask 中,URL 路由通常使用装饰器完成。这些装饰器可用于静态路由,以及带参数的 URL 路由。在以下示例中,假设此 Flask 脚本正在运行网站 www.example.com。
@app.route("/")
def index():
return "You went to www.example.com"
@app.route("/about")
def about():
return "You went to www.example.com/about"
@app.route("/users/guido-van-rossum")
def user():
return "You went to www.example.com/guido-van-rossum"
在最后一个路由中,你可以看到,给定一个带有 /users/ 和个人资料名称的 URL,我们可以返回一个个人资料。由于为每个用户包含一个 @app.route() 将极其低效且混乱,Flask 提供了从 URL 中获取参数的功能:
@app.route("/users/")
def profile(username):
return "Welcome to the profile of " + username
cities = ["OMAHA", "MELBOURNE", "NEPAL", "STUTTGART", "LIMA", "CAIRO", "SHANGHAI"]
@app.route("/stores/locations/")
def storefronts(city):
if city in cities:
return "Yes! We are located in " + city
else:
return "No. We are not located in " + city
4: HTTP 方法
最常见的两种 HTTP 方法是 GET 和 POST。Flask 可以根据使用的 HTTP 方法从同一个 URL 运行不同的代码。例如,在一个带有账户的 Web 服务中,将登录页面和登录过程通过同一个 URL 路由是最方便的。GET 请求(当你在浏览器中打开一个 URL 时执行的相同请求)应该显示登录表单,而 POST 请求(携带登录数据)应该单独处理。还创建了一个路由来处理 DELETE 和 PUT HTTP 方法。
@app.route("/login", methods=["GET"])
def login_form():
return "This is the login form"
@app.route("/login", methods=["POST"])
def login_auth():
return "Processing your data"
@app.route("/login", methods=["DELETE", "PUT"])
def deny():
return "This method is not allowed"
为了简化代码,我们可以从 Flask 导入 request 包。
from flask import request
@app.route("/login", methods=["GET", "POST", "DELETE", "PUT"])
def login():
if request.method == "DELETE" or request.method == "PUT":
return "This method is not allowed"
elif request.method == "GET":
return "This is the login forum"
elif request.method == "POST":
return "Processing your data"
要从 POST 请求中检索数据,我们必须使用 request 包:
from flask import request
@app.route("/login", methods=["GET", "POST", "DELETE", "PUT"])
def login():
if request.method == "DELETE" or request.method == "PUT":
return "This method is not allowed"
elif request.method == "GET":
return "This is the login forum"
elif request.method == "POST":
return "Username was " + request.form["username"] + " and password was " + request.form["password"]
Section 126.5: Jinja 模板
与 Meteor.js 类似,Flask 与前端模板服务集成得很好。Flask 默认使用 Jinja 模板。模板允许在 HTML 文件中使用小代码片段,例如条件语句或循环。
当我们渲染一个模板时,除了模板文件名之外的任何参数都将传递到 HTML 模板服务中。以下路由将用户名和加入日期(来自其他地方的函数)传递到 HTML 中。
@app.route("/users/")
def profile(username):
joinedDate = get_joined_date(username) # 此函数的代码无关紧要
awards = get_awards(username) # 此函数的代码无关紧要
# joinDate 是一个字符串,awards 是一个字符串数组
return render_template("profile.html", username=username, joinDate=joinedDate, awards=awards)
当此模板被渲染时,它可以使用从 render_template() 函数传递给它的变量。以下是 profile.html 的内容:
# if username
Profile of {{ username }}
# else
No User Found
# endif
{% if username %}
{{ username }} joined on the date {{ date }}
{% if len(awards) > 0 %}
{{ username }} has the following awards:
{% for award in awards %}
- {{award}}
{% endfor %}
{% else %}
{{ username }} has no awards
{% endif %}
{% else %}
No user was found under that username
{% endif %}
{# This is a comment and doesn't affect the output #}
以下分隔符用于不同的解释:
- {% ... %} 表示一个语句
- {{ ... }} 表示一个表达式,其中模板被输出
- {# ... #} 表示一个注释(不包括在模板输出中)
- {# ... ## 表示该行的其余部分应被解释为一个语句
Section 126.6: 请求对象
请求对象提供了有关向路由发出的请求的信息。要使用此对象,必须从 Flask 模块导入它:
from flask import request
URL 参数
在前面的示例中使用了 request.method 和 request.form,然而我们还可以使用 request.args 属性来检索 URL 参数中的键/值字典。
@app.route("/api/users/")
def user_api(username):
try:
token = request.args.get("key")
if key == "pA55w0Rd":
if isUser(username): # 此方法的代码无关紧要
joined = joinDate(username) # 此方法的代码无关紧要
return "User " + username + " joined on " + joined
else:
return "User not found"
else:
return "Incorrect key"
# 如果没有关键参数
except KeyError:
return "No key provided"
为了在此上下文中正确进行身份验证,需要以下 URL(将用户名替换为任何用户名):
www.example.com/api/users/guido-van-rossum?key=pa55w0Rd
文件上传
如果 POST 请求中提交的表单中包含文件上传,则可以使用请求对象处理文件:
@app.route("/upload", methods=["POST"])
def upload_file():
f = request.files["wordlist-upload"]
f.save("/var/www/uploads/" + f.filename) # 使用原始文件名存储
Cookie
请求还可以在与 URL 参数类似的字典中包含 Cookie。
@app.route("/home")
def home():
try:
username = request.cookies.get("username")
return "Your stored username is " + username
except KeyError:
return "No username cookies was found")