上一节我们开发了1个简单的Flask程序,并且知道它的最基本运行逻辑:创建1个程序实例,然后定义1个或多个路由,最后启动web服务器去轮询处理收到的请求。
或许此时你对“
程序实例收到请求并给予响应的过程”有些疑惑,我也是,下来我们一起探索。
程序和请求上下文
Flask程序实例收到从客户端发来的请求时,要让视图函数能访问一些对象,这样才能更好处理请求。
请求对象,就是1个很好的例子,它封装了客户端发送的HTTP请求。
要让视图函数能够访问请求对象,Flask使用上下文临时把某些对象变成全局可访问(仅单个线程)。有了上下文,就可以这样访问对象:
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
"""访问请求对象的请求头"""
user_agent = request.headers.get('User-Agent')
return '<h1>Your browser is %s.</h1>' % user_agent
if __name__ == '__main__':
app.run(debug=True)
在Flask中有两种上下文:程序上下文和请求上下文,一些说明如下:
变量名 | 上下文 | 说明 |
---|
current_app | 程序上下文 | 当前激活程序的程序实例 | g | 程序上下文 | 处理请求时用作临时存储的对象,每次请求都会重设这个变量 | request | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求的内容 | session | 请求上下文 | 用户会话,用于存储请求之间需要“记住”的值的字典 |
Flask在分发请求之前激活(或推送)程序和请求上下文,那么就可以使用上面几个变量了,且请求处理完成后会将它删除。 如果在使用全局变量前没有激活上下文,就会导致错误。下面在python shell会话演示程序上下文的使用方法:
(website) D:\Flask>python
......
>>> from hello import app
>>> from flask import current_app
>>> current_app.name # 没激活程序上下文之前就使用全局变量current_app,会导致错误
Traceback (most recent call last):
...
RuntimeError: working outside of application context
>>> app_ctx = app.app_context() # 获得1个程序上下文
>>> app_ctx.push() # 推送程序上下文
>>> current_app.name
'hello'
>>> app_ctx.pop() # 删除程序上下文
请求调度
程序收到客户端发来的请求时,要找到能处理该请求的视图函数。要完成这个动作,Flask会在程序的URL映射中查找请求的URL。
URL映射,是URL和视图函数的对应关系。 在Flask中,使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。
你想看看Flask程序中的URL映射是什么样子?我们可以在python shell会话中用app.url_map查看。
(venv) $ python
>>> from hello import app
>>> app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
/ 和 /user路由在程序中使用 app.route 修饰器定义。/static路由是Flask 添加的特殊路由,用于访问静态文件。 Flask为每个路由都指定了请求方法,这样在收到相同的URL、不同的请求方法,(可能)会使用不同的视图函数处理。在目前的例子中@app.route()都未指定methods值,但是有HEAD/OPTIONS/GET方法,可以看出这些是默认的,Flask提供了很大的便利!
请求钩子
有时我们需要在处理请求之前或者之后执行代码,比如创建数据库连接,这些会很有用。为了避免在每个视图函数使用重复的代码,我们可以用Flask提供的注册通用函数的功能,注册的函数即在处理请求之前或之后调用。 请求钩子使用修饰器实现,Flask支持下面4种钩子:
函数 | 说明 |
---|
before_first_request | 在处理第一个请求之前运行 | first_request | 在每次请求之前运行 | after_request | 如果没有未处理的异常抛出,在每次请求之后运行 | teardown_request | 即使有未处理的异常抛出,也在每次请求之后运行 |
在请求钩子函数和视图函数之间共享数据,一般使用上下文全局变量g。比如,before_request获取已登录用户,并将其保存到g.user,视图函数就可方便地使用已登录用户的数据。 请求钩子使用例子如下:
from flask import Flask, g
from datetime import datetime
app = Flask(__name__)
@app.before_request
def run_before_request():
"""访问请求对象的请求头"""
cur_time = time.strftime('%Y.%m.%d %H:%M:%S', time.localtime())
log_msg = "Request arrivered at %s, please note." % cur_time
g.begin_time = cur_time
print(log_msg)
@app.route('/')
def index():
return '<h1>Hello, Every.</h1><h2>Now is at %s.</h2>' % g.get('begin_time', '2000.10.1 9:00:00')
if __name__ == '__main__':
app.run(debug=True)
请求http://127.0.0.1:5000/后,服务器日志有run_before_request打印的内容,且请求到达的时间g.begin_time也共享了视图函数使用。
响应
大多数情况,响应是1个简单的字符串,像前面的例子。但HTTP协议需要的不仅是响应的字符串,还有1个重要的组成–状态码,Flask默认设为200,代表请求已被成功处理。 如果需要返回不同的状态码,则把状态码数字作为响应的第2个参数。例如:
@app.route('/error')
def bad_request():
return '<h1>Bad Request.</h1>', 400
返回的函数还可接受第3个参数,这是1个字典结构的响应头部(Header)。如果不想返回由 1 个、2 个或 3 个值组成的元组,Flask 视图函数还可以返回 Response 对象。make_response() 函数可接受 1 个、2 个或 3 个参数(和视图函数的返回值一样),并返回一个 Response 对象。有时我们需要在视图函数中进行这种转换,然后在响应对象上调用各种方法,进一步设置响应。
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>Tomorrow will come!</h1>')
response.set_cookie('gift', 'flower')
return response
有1种名为重定向的特殊响应类型,只告诉浏览器一个新地址用来加载新页面。重定向经常使用302状态码表示,指向的地址由location首部提供。 在Flask,提供了redirect()函数生成这种响应:
from flask import redirect
@app.route('/redirect')
def to_redirect():
return redirect('http://www.funny.com/new')
|