文章目录
Quickstart
A Minimal Application
最小的 Flask 应用程序如下所示:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
if __name__ == "__main__":
app.run()
代码解读:
- 首先,导入 Flask 类,这个类的一个实例就是我们的 WSGI 应用程序。
- 接下来,创建 Flask 类的实例。第一个参数
__name__
是应用程序的模块或者包的名称。这个参数必须要传,它的作用是让 Flask 知道在哪里查找模板和静态文件等资源。 - 然后使用
route()
装饰器告诉 Flask 哪个 URL 应该触发我们的函数。 - 该函数返回我们希望在用户浏览器中显示的消息。默认内容类型为 HTML,因此字符串中的 HTML 将由浏览器呈现。
将上面的程序另存为 hello.py
或者其他的名字也可以,但是不要将其命名为 flask.py
,因为它会与 Flask 本身冲突。
如何运行
要运行这个应用程序,请使用 flask
命令或 python -m flask
。需要使用 --app
选项告诉 Flask 你的应用程序在哪里:
flask --app hello run
如果你将程序命名为 app.py
或者 wsgi.py
,那么在运行的时候就不需要加 --app
选项了。
执行了上面的命令后,flask 会启动一个非常简单的内置服务器,如果用来做测试用,它足够你用了,但是如果你想把它当做生产环境的服务器,那是不太行的。
现在转到 http://127.0.0.1:5000,你就会看到 Hello World!
了。
修改端口号
如果你电脑上的另一个程序已经使用了端口 5000,则当服务器启动时,你将看到 OSError:[Errno 98]
或 OSError:[WinError 10013]
。你可以在运行时指定一个其他的端口号,如下将端口号改为 5001:
flask run --port=5001
设置为局域网内可访问
运行服务器,只有你自己的计算机能访问该服务器。在调试模式下,应用程序的用户可以在你的计算机上执行任意的 Python 代码。如果想让和你处在同一局域网内的其他用户可以访问你的服务器,只需要将 --host=0.0.0.0
添加到命令行即可使服务器公开可用:
flask run --host=0.0.0.0
Debug Mode
开启调试模式,服务器将在代码更改时自动重新加载,在请求期间发生错误时在浏览器中显示交互调试器。
**注意:**调试器运行从浏览器执行任意 Python 代码。这存在重大安全风险,所以不要在生产环境运行开发服务器或调试器。
要启用调试模式,只需要使用 --debug
选项。
(flaskenv) I:\CODE\flask_must>flask run --debug
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 834-696-848
HTML Escaping
在 Flask 中,默认的响应类型是 HTML。在输出中渲染任何用户提供的值时,必须对这些值进行转义,以防范注入攻击。例如:
from markupsafe import escape
@app.route("/<name>")
def hello(name):
return f"Hello, {escape(name)}!"
代码解读:
- 首先,从
markupsafe
模块导入escape
函数。markupsafe
是一个用于处理 HTML/XML 转义的库,它可以确保在将用户提供的数据插入到 HTML 中时,不会产生安全漏洞。 - 其次,
@app.route("/<name>")
是一个路由装饰器,它指定了一个 URL 路径,即"/<name>"
,其中"<name>"
是一个占位符,用于匹配用户在浏览器中访问的具体路径。例如,如果用户在浏览器中输入http://127.0.0.1:5000/Alice
,则 Flask 应用程序将执行与该路径匹配的函数。 def hello(name):
这是被路由装饰器修饰的函数。它接受一个名为name
的参数,这个参数是通过 URL 提供的用户输入。return f"Hello, {escape(name)}!"
:这行代码返回一个字符串作为响应。它使用了 f-string 格式化,将name
插入到 "Hello, " 和 “!” 之间。在插入name
之前,还使用escape
函数对其进行了转义,以确保任何特殊字符都被正确地转义,从而避免在生成 HTML 时出现潜在的安全问题。
Routing
使用有意义的URL对于用户体验和网站的可用性非常重要。
使用 route()
装饰器将函数绑定到 URL。
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World'
Variable Rules
在URL中可以使用 <variable_name>
的形式来标记可变的部分。然后,你的函数可以将 <variable_name>
作为关键字参数接收。如果需要,你还可以使用转换器来指定参数的类型,例如 <converter:variable_name>
。
这种URL模式称为可变路由(Variable Route),它允许在URL中指定一些可变的部分,以便根据不同的输入动态地处理请求。以下是一些说明:
<variable_name>
:使用尖括号括起来的部分表示变量,例如<name>
。这个变量会从URL中提取出来,并作为关键字参数传递给相应的函数。<converter:variable_name>
:如果需要,你可以在变量前面使用转换器指定参数的类型。转换器是可选的,用于将变量值转换为特定的数据类型。例如,<int:id>
表示将变量id
转换为整数类型。
通过使用可变路由,你可以根据不同的URL结构和参数来定义不同的处理逻辑。例如,如果URL是 /user/Alice
,你可以使用 <name>
变量来提取用户名,然后将其作为关键字参数传递给相应的函数。
以下是一些示例:
from markupsafe import escape
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return f'User {escape(username)}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return f'Post {post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return f'Subpath {escape(subpath)}'
下面是一些常见的转换器:
转换器 | 含义 |
---|---|
string | (默认值)接受不带斜杠的任何文本 |
int | 接受正整数 |
float | 接受正浮点值 |
path | 像 string ,但也接受斜杠 |
uuid | 接受 UUID 字符串 |
Unique URLs / Redirection Behavior
以下两个规则在使用尾部斜杠方面有所不同。
@app.route('/projects/')
def projects():
return 'The project page'
@app.route('/about')
def about():
return 'The about page'
对于 “projects” 端点(endpoint)的规范URL(canonical URL),它以斜杠(/)结尾,类似于文件系统中的文件夹。如果你访问该URL时没有斜杠结尾(例如,/projects),Flask会将你重定向到带有斜杠结尾的规范URL(例如,/projects/)。
对于 “about” 端点(endpoint)的规范URL(canonical URL),它不以斜杠(/)结尾,类似于文件的路径名。如果你访问该URL时带有斜杠结尾(例如,/about/),会产生一个404 "Not Found"错误。这有助于确保这些资源的URL唯一性,避免搜索引擎将同一页面索引两次。
URL Building
为了构建指向特定函数的URL,可以使用 url_for()
函数。它接受函数的名称作为第一个参数,以及任意数量的关键字参数,每个参数对应URL规则中的一个变量部分。未知的变量部分将作为查询参数附加到URL上。
为什么要使用 URL 反转函数 url_for()
构建 URL,而不是将它们硬编码到模板中?
- 反向通常比硬编码 URL 更具描述性。
- 可以一次性更改 URL,而无需记住手动更改硬编码的 URL。
- URL 构建以透明方式处理特殊字符的转义。
- 生成的路径始终是绝对路径,避免了浏览器中相对路径的意外行为。
- 如果您的应用程序放置在 URL 根目录之外,例如,在
/myapplication
而不是/
中,url_for()
会为您正确处理。
例如,这里我们使用 test_request_context()
方法来尝试 url_for()
. test_request_context()
告诉 Flask 即使我们使用 Python shell 时,它也要表现得好像在处理请求一样。
from flask import url_for
@app.route('/')
def index():
return 'index'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return f'{username}\'s profile'
with app.test_request_context():
print(url_for('index'))
print(url_for('login'))
print(url_for('login', next='/'))
print(url_for('profile', username='John Doe'))
输出结果:
/
/login
/login?next=/
/user/John%20Doe
HTTP Methods
在访问URL时,Web应用程序使用不同的HTTP方法(HTTP methods)。在使用Flask时,你应该熟悉HTTP方法的使用。默认情况下,路由(route)只会响应GET请求。你可以使用route()
装饰器的methods
参数来处理不同的HTTP方法。
HTTP定义了一些常见的方法,用于指定客户端对服务器端的请求类型。其中一些常见的HTTP方法包括:
- GET:用于从服务器获取资源。当用户在浏览器中输入URL、单击链接或提交HTML表单时,通常使用GET方法。
- POST:用于向服务器提交数据。当用户提交表单、上传文件或进行身份验证等操作时,通常使用POST方法。
- PUT:用于向服务器发送数据,用于创建或更新资源。PUT方法通常用于更新整个资源。
- DELETE:用于从服务器删除资源。
- PATCH:用于部分更新资源。与PUT方法不同,PATCH方法用于更新资源的一部分。
在Flask中,默认情况下,路由(route)只会响应GET请求。但是,你可以通过使用methods
参数来指定路由应该响应的HTTP方法。methods
参数可以接受一个包含HTTP方法的列表,以指定路由可以处理的多个HTTP方法。
from flask import Flask
app = Flask(__name__)
@app.route("/data", methods=["GET", "POST"])
def handle_data():
if request.method == "GET":
# 处理GET请求的逻辑
return "This is a GET request."
elif request.method == "POST":
# 处理POST请求的逻辑
return "This is a POST request."
if __name__ == "__main__":
app.run()
你还可以将不同HTTP方法的视图(views)分别放置在不同的函数中。Flask提供了一种快捷方式,用于为每个常见的HTTP方法(如GET、POST等)单独装饰对应的路由(route)。
Flask为每个常见的HTTP方法(GET、POST、PUT、DELETE等)提供了相应的装饰器,用于装饰不同方法的路由。这些装饰器的命名与HTTP方法相对应,例如@app.route
的get()
、post()
、put()
和delete()
等。
以下是一个示例:
from flask import Flask
app = Flask(__name__)
@app.get('/login')
def login_get():
return show_the_login_form()
@app.post('/login')
def login_post():
return do_the_login()
if __name__ == "__main__":
app.run()
如果Flask应用程序中有对GET方法的处理逻辑,Flask会自动为你添加对HEAD方法的支持,并按照HTTP规范处理HEAD请求。此外,Flask还会为你自动实现OPTIONS方法的支持,使你能够获得有关服务器功能和可用方法的信息。这样的设计使得处理HEAD和OPTIONS请求变得简单和一致。
Static Files
动态的 Web 应用程序通常需要静态文件,如 CSS 和 JavaScript 文件。在理想情况下,你的 Web 服务器会配置为为你提供这些静态文件的服务。但在开发过程中,Flask 也可以帮助你提供静态文件的服务。只需在你的包(package)中或与你的模块(module)相邻的位置创建一个名为 static
的文件夹,它将在应用程序中通过 /static
路径访问。
例如,如果你有一个名为 myapp
的包(package),并在其中创建了一个 static
文件夹,你的静态文件的路径将会是 /static
。假设你有一个名为 style.css
的 CSS 文件在 static
文件夹中,你可以通过以下方式在 HTML 文件中引用它:
<link rel="stylesheet" href="/static/style.css">
若要为静态文件生成 URL,请使用特殊的 'static'
终结点名称:
url_for('static', filename='style.css')
该文件必须作为 static/style.css
存储在文件系统中。
Rendering Templates
在Python中直接生成HTML确实不太方便,而且相当繁琐,因为你必须自己处理HTML转义以确保应用程序的安全性。正因如此,Flask会自动配置Jinja2模板引擎,简化了生成HTML的过程。
Jinja2是一个强大且灵活的模板引擎,它与Flask紧密集成。通过使用Jinja2模板引擎,你可以轻松地编写动态的HTML模板,减少了手动处理HTML转义的负担,并确保应用程序的安全性。
在Flask中,你可以使用render_template()
方法来渲染模板。你只需要提供模板的名称以及要传递给模板引擎的变量作为关键字参数。下面是一个简单的例子,演示如何渲染模板:
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
Flask 将在 templates
文件夹中查找模板。因此,如果您的应用程序是一个模块,则此文件夹位于该模块旁边,如果它是一个包,则它实际上在您的包内:
Case1:模块:
/application.py
/templates
/hello.html
Case2:包:
/application
/__init__.py
/templates
/hello.html
下面是一个示例模板:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
在模板中,您还可以访问 config
、 request
、 session
和 g
对象以及 url_for()
和 get_flashed_messages()
函数。
如果使用继承,模板特别有用。如果想知道其工作原理,请参阅模板继承 。基本上,模板继承可以在每个页面上保留某些元素(如页眉,导航和页脚)。
自动转义已启用,因此如果 name
包含 HTML,则会自动转义。如果您可以信任某个变量,并且您知道它将是安全的 HTML(例如,因为它来自将 wiki 标记转换为 HTML 的模块),则可以通过使用 Markup
类或使用模板中的 |safe
筛选器将其标记为安全。
下面是对 Markup
类工作原理的基本介绍:
>>> from markupsafe import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup('<strong>Hello <blink>hacker</blink>!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup('<blink>hacker</blink>')
>>> Markup('<em>Marked up</em> » HTML').striptags()
'Marked up » HTML'
Accessing Request Data
对于 Web 应用程序,对客户端发送到服务器的数据做出反应至关重要。在 Flask 中,此信息由全局 request
对象提供。如果你有一些使用Python的经验,你可能想知道这个对象是如何全局的,以及Flask是如何设法仍然是线程安全的。答案是上下文局部: