Flask 入门教程

QuickStart

1.安装

https://flask.palletsprojects.com/en/2.3.x/installation/

 conda install flask

2.flask 应用示例

  1. import flask
  2. 创建 flask 对象,构造参数是应用的包名
  3. route 是告诉 flask,URL 触发函数
  4. 函数返回想要在浏览器中显示的信息,默认是 html
  5. 调用 app.run 启动应用

代码:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

if __name__ == '__main__':
        app.run(host="127.0.0.1", port="5200")

结果:
image.png
端口冲突:
OSError: [Errno 98] 或者 OSError: [WinError 10013]

开发

1.路由(Routing)

现代web应用可以通过有意义的路由来绑定页面,帮助用户

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello, World'
请求方法(Http Method)

flask 可以指定路由函数的访问方法,默认是 Get

  • 指定单个路由的多个方法
from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()
  • 指定单个路由的单个方法
@app.get('/login')
def login_get():
    return show_the_login_form()

@app.post('/login')
def login_post():
    return do_the_login()

如果存在 GET, Flask 会自动添加对 HEAD 方法的支持,并根据 HTTP RFC 处理 HEAD 请求。

变量规则

可以通过使用<variable_name>标记区段来向URL添加可变区段。
然后,函数接收<variable_name>作为关键字参数。
还可以选择使用转换器来指定参数的类型,例如converter:variable_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(default) accepts any text without a slash
intaccepts positive integers
floataccepts positive floating point values
pathlike string but also accepts slashes
uuidaccepts UUID strings
末尾正斜杠

下文代码中两个路由的区别在于末尾正斜杠
对于 @app.route(‘/projects/’),若访问 /projects 会自动重定向到 /projects/
对于 @app.route(‘/about’),若 /about/ 不会重定向到 /about ,更具有唯一性

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'
Url 构造

使用 url_for 构造指向特定函数的URL,它接受函数名作为第一个参数和任意数量的关键字参数,每个参数对应于URL规则的一个变量部分。未知变量部分作为查询参数追加到URL。

  1. url_for 通常比硬编码 url 更具描述性。
  2. 一次性更改url,而无需手动更改硬编码的url
  3. URL构建透明地处理特殊字符的转义
  4. 生成的路径总是绝对的,避免了浏览器中相对路径的意外行为
  5. 如果应用程序位于URL根目录之外,例如,在/myapplication中而不是/中,url_for()将正确地执行给你

代码:

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

2.HTML渲染

从 Python 内部生成相当麻烦,因为您必须自己进行HTML转义以保证应用程序的安全。
因此,Flask 会自动为您配置 Jinja2 模板引擎。模板可用于生成任何类型的文本文件。对于 web 应用程序,您将主要生成 HTML 页面,但您也可以生成标记,电子邮件的纯文本和其他任何内容。
https://jinja.palletsprojects.com/en/3.1.x/templates/
有关 HTML、CSS 和其他 web api 的参考,请参阅 MDN web Docs。
https://developer.mozilla.org/zh-CN/
要渲染模板,你可以使用 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)

render_template 会自动渲染 hello.html,如果 hello.html 在对应的 templates 文件夹下

  1. 文件
/application.py
/templates
    /hello.html
/application
    /__init__.py
    /templates
        /hello.html

在模板内部,您还可以访问配置、请求、会话和 gil 对象以及url_for()和 get_flashd_messages() 函数。模板在使用继承时特别有用。如果你想知道它是如何工作的,请参阅模板继承。https://flask.palletsprojects.com/en/2.3.x/patterns/templateinheritance/
基本上,模板继承使得在每个页面上保留某些元素 (如页眉、导航和页脚) 成为可能。
自动转义是启用的,所以如果 name 包含 HTML,它将自动转义。如果您可以信任一个变量,并且您知道它将是安全的 HTML(例如,因为它来自将wiki标记转换为HTML的模块),您可以通过使用markup类或在模板中使用Isafe过滤器将其标记为安全。查看Jinja 2文档了解更多示例。下面是对Markup类工作原理的基本介绍:

from markupsafe import Markup
Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup('<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
Markup.escape('<blink>hacker</blink>')
Markup('&lt;blink&gt;hacker&lt;/blink&gt;')
Markup('<em>Marked up</em> &raquo; HTML').striptags()
'Marked up » HTML'

3.获取请求参数(Accessing Request Data)

对于web应用程序,对客户端发送到服务器的数据做出响应是至关重要的。在Flask中,这个信息由全局请求对象提供。如果你有一些Python经验,你可能想知道这个对象是如何成为全局的,以及Flask如何仍然是线程安全的。答案是上下文局部变量:

上下文局部变量

Flask中的某些对象是全局对象,但不是通常的那种。这些对象实际上是特定上下文的本地对象的代理。但这其实很容易理解。
假设上下文是处理线程。一个请求来了,web服务器决定生成一个新线程(或者其他什么东西,底层对象能够处理线程以外的并发系统)。当Flask启动内部请求处理时,它会发现当前线程是活动上下文,并将当前应用程序和WSGI环境绑定到该上下文(线程)。
它以一种智能的方式做到这一点,以便一个应用程序可以调用另一个应用程序而不会中断。这对你来说意味着什么?基本上你可以完全忽略这种情况,除非你在做单元测试之类的事情。您将注意到依赖于请求对象的代码会突然中断,因为没有请求对象。
解决方案是自己创建一个请求对象并将其绑定到上下文。单元测试最简单的解决方案是使用
test_request_context()上下文管理器。结合with语句,它将绑定一个测试请求,以便您可以与它交互。下面是一个例子:

from flask import request

with app.test_request_context('/hello', method='POST'):
    # now you can do something with the request until the
    # end of the with block, such as basic assertions:
    assert request.path == '/hello'
    assert request.method == 'POST'
请求对象(The Request Object)
获取 query params 参数

通过 request 对象获取传递参数
https://flask.palletsprojects.com/en/2.3.x/api/#flask.Request

from flask import request

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

使用 request.args.get() 会自动捕获 error,不需要手动捕获 error

searchword = request.args.get('key', '')
获取 json 参数

通过 request 对象获取传递的 json 参数

from flask import request

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.json['username'],
                       request.json['password']):
            return log_the_user_in(request.json['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)
文件上传到 falsk

https://flask.palletsprojects.com/en/2.3.x/patterns/fileuploads/
你可以用Flask轻松地处理上传的文件。只要确保不要忘记在HTML表单上设置 “multipart/form-data” 属性,否则浏览器根本不会传输你的文件。
上传的文件存储在内存中或文件系统上的临时位置。您可以通过查看请求对象上的 files 属性来访问这些文件。每个上传的文件都存储在该字典中。它的行为就像一个标准的Python文件对象,但它也有一个save()方法,允许您将该文件存储在服务器的文件系统上。下面是一个简单的例子:

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果想知道文件上传到应用程序之前在客户机上是如何命名的,可以访问 filename 属性。但是请记住,这个价值是可以伪造的,所以永远不要相信这个价值。如果你想使用客户端的文件名在服务器上存储文件,通过 Werkzeug 提供的 secure_filename() 函数传递它:

from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['the_file']
        file.save(f"/var/www/uploads/{secure_filename(file.filename)}")
    ...
Cookies

要访问cookie,可以使用cookie属性。要设置cookie,可以使用响应对象的set_cookie方法。请求对象的cookie属性是一个包含客户端传输的所有cookie的字典。如果你想使用会话,不要直接使用cookie,而是使用Flask中的会话,它为你在cookie上添加了一些安全性。
获取 cookie 参数

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

设置 cookie 参数

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

4.重定向和错误页

要将用户重定向到另一个端点,请使用redirect()函数;要提前终止带有错误码的请求,请使用abort()函数:

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

用户将从索引重定向到他们无法访问的页面(401表示拒绝访问),但它展示了如何工作。默认情况下,每个错误代码显示一个黑白错误页面。如果你想定制错误页面,你可以使用errorhandler()装饰器:

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

5.响应(Response)

函数的返回值会自动转换为响应对象。如果返回值是字符串,则将其转换为响应对象,其中包含字符串作为响应主体、200 状态码和 text/ html 类型。如果返回值是字典或列表,则调用 jsonify()来生成响应。Flask将返回值转换为响应对象的逻辑如下:

  1. 如果返回了正确类型的响应对象,它将直接从函数返回。
  2. 如果是字符串,则使用该数据和默认参数创建响应对象。
  3. 如果它是返回字符串或字节的迭代器或生成器,则将其视为流响应。
  4. 如果它是一个字典或列表,则使用 jsonify() 创建响应对象。
  5. 如果返回元组,则元组中的项可以提供额外的信息。这样的元组必须采用(response, status)、(response, headers) 或 (response, status, headers) 的形式。状态值将覆盖状态码,报头可以是附加报头值的列表或字典。
  6. 如果这些都不起作用,Flask将假定返回值是一个有效的WSGI应用程序,并将其转换为响应对象。
make_response

可以使用 make_response 自己构造 response
https://flask.palletsprojects.com/en/2.3.x/api/#flask.make_response

from flask import render_template

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

使用 make_response 重写

from flask import make_response

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp
json

编写API时,常见的响应格式是JSON。用Flask编写这样的API很容易。如果从视图返回字典或列表,它将被转换为json响应。
这是将数据传递给jsonify()函数的快捷方式,该函数将序列化任何受支持的JSON数据类型。这意味着字典或列表中的所有数据必须是JSON可序列化的。对于复杂类型(如数据库模型),您需要首先使用序列化库将数据转换为有效的JSON类型。社区维护了许多序列化库和Flask API扩展,以支持更复杂的应用程序。

@app.route("/me")
def me_api():
    user = get_current_user()
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image),
    }

@app.route("/users")
def users_api():
    users = get_all_users()
    return [user.to_json() for user in users]

6.静态文件

动态web应用程序也需要静态文件。这通常是 CSS 和 JavaScript 文件的来源。
理想情况下,您的 web 服务器被配置为为您服务,但在开发期间 Flask 也可以做到这一点。
只需在包中或模块旁边创建一个名为 static 的文件夹,它将在应用程序的 /static 处可用。要为静态文件生成 url,使用特殊的 static 端点名称:

// 文件需要方法 static 文件夹下
url_for('static', filename='style.css')

7.会话

除了请求对象之外,还有第二个对象称为session,它允许您从一个请求到下一个请求存储特定于用户的信息。这是在 cookie 之上实现的,并对 cookie 进行加密签名。这意味着用户可以查看 cookie 的内容,但不能修改它,除非他们知道用于签名的秘密密钥。为了使用会话,您必须设置一个密钥。以下是会话的工作原理:

from flask import session

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {session["username"]}'
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))
会话密钥

密钥应该尽可能随机。您的操作系统可以基于加密图形随机生成器生成相当随机的数据。使用以下命令快速为Flask生成一个值。secret_key:

$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

关于基于cookie的会话的注意事项:Flask将获取您放入会话对象中的值并将其序列化到cookie中。如果你发现一些值不能在请求中持续存在,cookie确实是启用的,并且你没有得到一个明确的错误信息,那么请检查你的页面响应中cookie的大小,并将其与web浏览器支持的大小进行比较。

8.日志

有时您可能会遇到这样的情况:您处理的数据应该是正确的,但实际上并非如此。例如,您可能有一些向服务器发送HTTP请求的客户端代码,但它显然是错误的。这可能是由用户引起的篡改数据,或客户端代码失败。大多数情况下,在这种情况下回复 400 错误请求是可以的,但有时这行不通,代码必须继续工作。你可能仍然想记录一些可疑的事情发生了。这就是日志派上用场的地方。从Flask 0.3开始,已经预先配置了日志记录器供您使用。下面是一些日志调用的例子:

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

9.HTML转义

由于返回值的默认类型是HTML,所以为了防止被用户注入,所以用户传递的参数在返回值中必须被转义
方法为使用 escape 函数
代码:

from flask import Flask, request
from markupsafe import escape

app = Flask(__name__)

@app.route("/name")
def hello():
    name = request.json['name']
    return f"Hello, {escape(name)}!"

if __name__ == '__main__':
    app.run(host="127.0.0.1", port="5200", debug=True)

测试:
注入一段 alter(“bad”) 的代码
image.png
若没有通过转义:
会触发注入代码
image.png
若通过转义:
注入代码会被转义成字符串
image.png

10.调试模式

  • 代码发生修改,会自动重启服务
  • 报错时,浏览器会显示报错堆栈信息,并可以调试

代码:
image.png
调试模式:
image.png
报错堆栈:
image.png

11.Message Flashing

Flask提供了一种非常简单的方法,通过闪烁系统向用户提供反馈。闪烁系统基本上使得在请求结束时记录消息并在下一个(且仅下一个)请求时访问它成为可能。这通常与一个布局模板相结合,以公开消息。
https://flask.palletsprojects.com/en/2.3.x/patterns/flashing/

12.部署到生产环境

https://flask.palletsprojects.com/en/2.3.x/deploying/#deploying-to-production

13.Hooking in WSGI Middleware

要将WSGI中间件添加到Flask应用程序中,请包装应用程序的wsgi_app属性。例如,要应用Werkzeug的ProxyFix中间件运行在Nginx之后:
https://werkzeug.palletsprojects.com/en/2.3.x/middleware/proxy_fix/#werkzeug.middleware.proxy_fix.ProxyFix

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)

14.Using Flask Extensions

https://flask.palletsprojects.com/en/2.3.x/extensions/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值