2.2 请求与响应¶
2.2.1 应用和请求上下文¶
Flask 从客户端收到请求时,要让视图函数能访问一些对象,才能处理请求。要想让视图函数能访问请求对象,一种直接方式是将其作为参数传入视图函数,不过会导致每个视图函数都会多出一个参数。除了访问请求对象,如果还要访问其他对象,情况便的糟糕。为了避免这种情况, Flask 使用上下文机制临时把某些对象变为全局可访问。
在 Flask 中有两种上下文:应用上下文(application context)和请求上下文(request context)。
Flask 上下文全局变量:
变量名 |
上下文 |
说明 |
---|---|---|
|
应用上下文 |
当前应用的应用实例 |
|
应用上下文 |
处理请求时用 作临时存储的对象,每次请求都会重设 |
|
请求上下文 |
请求对象,封装了客户端发出的 HTTP 请求中的内容 |
|
请求上下文 |
用户会话,值为一 个字典,存储请求之间需要“记住”的值 |
Flask
在分派请求之前激活(或推送)应用和请求上下文,请求处理完成后再将其删除。应用上下文被推送后,就可以在当前线程中使用
current_app
和 g
变量。请求上下文被推送后,就可以使用
request
和 session
变量。如果使用这些变量时没有激活应用和请求上下文,就会导致错误。
获取应用上下文的方法是在应用实例上调用 app.app_context()
:
from flask import Flask, current_app
app = Flask(__name__)
with app.app_context():
print(current_app.name) # __main__
2.2.2 请求分派¶
应用收到客户端发送的请求时,要找到处理该请求的视图函数。Flask 在应用的 URL 映射中查找请求的 URL。URL 映射是 URL 和视图函数间的对应关系。Flask 使用 app.route 装饰器构建映射。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello, World!</h1>'
print(app.url_map)
# 输出
Map([<Rule '/' (OPTIONS, GET, HEAD) -> index>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])
输出中的 /
路由是在应用中使用app.route
定义,/static/<filename>
路由是 Flask
添加的特殊路由,用于访问静态文件。 URL 映射中的 (OPTIONS, GET, HEAD)
是请求方法。
2.2.3 请求对象¶
Flask 通过上下文变量 request 对外开放请求对象,包含客户端发送的 HTTP 请求的全部信息。使用时需要从 flask 模块中导入请求对象:
from flask import request
请求对象中常用的属性和方法:
属性或方法 |
说明 |
---|---|
form |
一个字典,存储请求提交的所有表单字段 |
args |
一个字典,存储通过 URL 查询字符串传递的所有参数 |
values |
一个字典,form 和 args 的合集 |
cookies |
一个字典,存储请求的所有 cookie |
headers |
一个字典,存储请求的所有 HTTP 首部 |
files |
一个字典,存储请求上传的所有文件 |
get_data() |
返回请求主体缓存的数据 |
blueprint |
处理请求的 Flask 蓝本的名称 |
endpoint |
处理请求的 Flask 端点名称 |
method |
HTTP 请求方法,如 GET 和 POST |
scheme |
URL 方案(http 或 https) |
is_secure() |
返回安全的连接(HTTPS)发送请求时返回 True |
host |
请求定义的主机名,如果客户端定义了端口号,还包括端口号 |
path |
URL 的路径部分 |
url |
客户端请求的完整 URL |
base_url |
同 url,但没有查询字符串部分 |
remote_addr |
客户端的 IP 地址 |
示例1:使用
form
属性处理表单数据
@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'
return render_template('login.html', error=error)
示例2:使用
args
属性操作 URL (如?key=value
)中提交的参数
searchword = request.args.get('key', '')
2.2.4 请求钩子¶
有时在处理请求之前或之后有一些工作需要统一处理,比如创建数据库连接、验证用户身份等。为了避免在每个视图函数中都重复编写代码,Flask 提供了注册通用函数的功能,即请求钩子。注册的函数可在请求被反派到视图函数之前或之后调用。
请求钩子通过装饰器实现。Flask 支持以下4中钩子:
before_request
:注册一个函数,在每次请求之前运行。before_first_request
:注册一个函数,只在处理第一个请求之前运行。可以通过这个钩子添加服务器初始化任务。after_request
:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。teardown_request
:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def before_first_request():
print('before_first_request')
@app.before_request
def before_request():
print('before_request')
@app.after_request
def after_request(response):
print('after_request')
return response
@app.teardown_request
def teardown_request(exc):
print('teardown_request')
@app.route("/")
def index():
return '<h1>Hello, World!</h1>'
if __name__ == '__main__':
app.run(debug=True)
2.2.5 响应¶
Flask
调用视图函数后,会将其返回值作为响应的内容。视图函数的返回值会自动转换为一个响应对象。如果返回值是一个字符串,那么会被转换为一个包含作为响应体的字符串、一个出错代码
和一个 text/html
类型的响应对象。如果返回值是一个字典,那么会调用
jsonify()
来产生一个响应。转换规则如下:
如果视图返回的是一个响应对象,则直接返回。
如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的响应对象。
如果返回的是一个字典,那么调用
jsonify
创建一个响应对象。
响应对象常使用的属性和方法:
属性或方法 |
说明 |
---|---|
status_code |
HTTP 数字状态码 |
headers |
一个类似字典的对象,包含随响应发送的所有首部 |
set_cookie() |
为响应添加一个 cookie |
delete_cookie() |
删除一个 cookie |
content_length |
响应主体的长度 |
content_type |
响应主体的媒体类型 |
set_data() |
使用字符串或字节值设定响应 |
get_data() |
获取响应主体 |
示例一:返回字符串:
如果返回的响应需要使用状态码,可以作为第二个返回值,添加在响应字符串之后。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Bad Request!</h1>', 400
if __name__ == '__main__':
app.run()
示例二:返回模板文件
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def error():
return render_template("error.html"), 404
if __name__ == '__main__':
app.run()
示例三:直接返回响应对象
使用 make_response()
创建一个响应对象。
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/')
def index():
response = make_response('<h1>Hello, World!</h1>')
return response
if __name__ == '__main__':
app.run()
示例四:重定向
重定向是响应的特殊类型,会告诉浏览器一个新的 URL,用以加载新页面。
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/')
def index():
return redirect('http://www.baidu.com')
if __name__ == '__main__':
app.run()
示例五:jsonify 返回响应对象
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def index():
d = {'name': 'lin', 'age': '27'}
return jsonify(d)
if __name__ == '__main__':
app.run()