route装饰器解析
变量名规则:RESTful风格的URL
重定向和错误页处理
请求
响应
session
日志
WSGI中间件
文件上传
静态文件
route路由解析
route函数的实现如下:
def route(self, rule: str, **options: t.Any) -> t.Callable:
def decorator(f: t.Callable) -> t.Callable:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
可以发现route()主要作用是装饰了一个add_url_rule()的函数
而add_url_rule()才是设置路由的函数
如下代码证明:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
def index():
return 'index'
app.add_url_rule('/index', view_func=index)# view_func为视图函数,路由必填
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
run后在浏览器测试http://127.0.0.1:5000/index
可以发现 确实!
变量名规则
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route('/hello/<who>')
def hello_world(who):
return 'Hello World!'+who
@app.route('/index/<string:key>')
def index(key):
return 'index'+key
app.add_url_rule('/index', view_func=index)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
类似restful风格的路由
参数规则中,变量名默认是string类型
路由格式:<variable_name>
或<converter:variable_name>
变量名类型有如下:
string:(缺省值) 接受任何不包含斜杠的文本
int:接受正整数
float:接受正浮点数
path:类似 string ,但可以包含斜杠
uuid:接受 UUID 字符串
视图函数返回值
只可以返回如下类型变量:
string
dict
tuple
Response instance
WSGI callable
重定向和错误
url构建
url_for()把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。
我的理解是:第一个参数是视图函数的函数名,第二个参数是(路由变量名=value)的形式
from flask import url_for
app = Flask(__name__)
@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
重定向到错误页面示例
使用 redirect() 函数可以重定向。使用 abort() 可以 更早退出请求,并返回错误代码
每种出错代码都会对应显示一个黑白的出错页面。使用 errorhandler() 装饰器可以定制出错页面
render_template() 方法可以渲染模板,您只要提供模板名称和需要作为参数传递给模板的变量就行。例如,return render_template('hello.html', name=name)
,此时html页面可以接受name变量,展示示例:
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
# settings.py
DEBUG=True
<!-- not_login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>未登录</title>
</head>
<body>
<h1>未登录</h1>
</body>
</html>
# app.py
import logging
from flask import Flask, redirect, url_for, abort, render_template
import settings
app = Flask(__name__)
app.config.from_object(settings)
@app.route("/")
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(403)#
@app.errorhandler(403)# 错误页路由
def not_login(error):
logging.error(error)
return render_template('not_login.html'), 403# render_template() 后面的 403是响应状态码 。缺省情况下 200 表示:一切正常。
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
访问网址:http://127.0.0.1:5000
将看到“未登录”的h1字体
render_template() 方法可以渲染模板,您只要提供模板名称和需要 作为参数传递给模板的变量就行了。
请求
@app.route("/")
def index():
logging.error("method:" + request.method)
if request.method == "GET":
form = request.args if request.args else None
elif request.method == "POST":
form = request.form if request.form else None# 当 form 属性中不存在这个键时会发生什么?会引发一个 KeyError
if form:
logging.error("form:" + form)
url后面有参数时,可以通过args.get获取,比如searchword = request.args.get('key', '')
推荐使用 get 或通过捕捉 KeyError 来访问 URL 参数。
扩展cookie
username = request.cookies.get('username')
可以使用响应 对象 的 set_cookie 方法来设置 cookies 。(响应对象在下面会介绍到,可以看完以下内容再回顾cookie,也可以跳过cookie,resp.set_cookie('username', 'the username')
)
请求对象的 cookies 属性是一个包含了客户端传输的所有 cookies 的字典。可以通过username = request.cookies.get('username')
调用
响应
视图函数的返回值会自动转换为一个响应对象。那么如果不是响应对象则将返回对象转换为响应对象
转换规则:
- 如果视图返回的是一个响应对象,那么就直接返回它。
- 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个包含作为响应体的字符串、一个 200 OK 出错代码 和一个 text/html 类型的响应对象。
- 如果返回的是一个字典,那么调用 jsonify 创建一个响应对象。
- 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项,且项应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。
- 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
# jsonify使用样例
jsonify([user.to_json() for user in users])
如果想要在视图内部掌控响应对象的结果。可以使用 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
session会话,比cookie更安全
session 对象相当于用密钥签名加密的 cookie ,即用户可以查看您的 cookie ,但是如果没有密钥就无法修改它。
那么如何生成密钥?可以快捷的为 Flask.secret_key ( 或者 SECRET_KEY )生成值:
$ python -c 'import os; print(os.urandom(16))'
b'_5#y2L"F4Q8z\n\xec]/'
那么会话可以进行如下使用:
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 0.3 后配置 了一个日志工具logger
app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')
WSGI中间件
要在应用中添加一个 WSGI 中间件,那么可以用应用的 wsgi_app 属性 来包装。例如,假设需要在 Nginx 后面使用 ProxyFix 中间件,那么可以这样做:
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
用 app.wsgi_app 来包装,而不用 app 包装,意味着 app 仍旧指向您 的 Flask 应用,而不是指向中间件。这样可以继续直接使用和配置 app 。
文件上传
不要忘记在您的 HTML 表单中设置 enctype=“multipart/form-data” 属性
可以通过请求对象 files 属性来访问上传的文件。每个上传的文件都储存在这个 字典型属性中。这个属性基本和标准 Python file 对象一样,另外多出一个 用于把上传文件保存到服务器的文件系统中的 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)}")
...
静态文件
静态文件,一般是 CSS 和 JavaScript 文件。只要在包或模块旁边创建一个名为 static 的文件夹就行了。 静态文件位于应用的 /static 中。例如:url_for('static', filename='style.css')
在文件系统中的位置应该是 static/style.css
其他
此类为扩展知识,自行了解
flash()