42Team-Flask框架-请求与响应

请求-响应循环

上一个章节写了一个简单的Flask程序,那么Flask的工作方式你是否好奇?下面会简单介绍一些Flask框架的设计理念。

应用和请求上下文

Flask收到来自浏览器客户端的请求后,需要让视图函数获取到一些对象,这样才能更好地处理请求。所以Flask用请求对象来封装浏览器发送的HTTP请求,Flask使用请求上下文来临时的把某些对象变为全局可访问的对象。有了请求上下文,就可以获取得到浏览器客户端发给服务器端的HTTP请求了,其中抱恨用户提交的表单、请求头等。

1
2
3
4
5
6
7

from flask import Flask,request

@app.route('/request')
def request_test():
user_agent = request.headers.get('User-Agent')
return 'Your Browser info : {}'.format(user_agent)

request.headers.get('User-Agent')可以获得客户端使用的浏览器的一些基本信息。

user-agent-2020-04-26

如果你想用request,需要从flask中导入request。你需要注意的是在这个视图函数中,我们把request当作一个全局变量来使用了。事实上,request不可能是全局变量,假设这时候有很多用户同时访问我们的服务。如果request是全局变量的话就会导致数据混乱,所以在多线程服务器中,每个线程同时处理着不同用户客户端发过来的数据,那么必然每个线程所使用的request也是不同的。Flask使用上下文让特定变量在一个线程在他的生面周期内可以访问,并且不会干扰其他线程。

线程是可单独管理的最小指令集,进程通常使用多个活动线程,有时候还会共享内存和文件句柄等其他资源。通常多线程Web服务器例如Flask会创建一个线程池,如果有客户访问服务器,那么会从线程池中抽出一个线程处理客户端发出的请求。

在Flask中有两种上下文:

  • 应用上下文
  • 请求上下文
变量名 类型 说明
request 请求上下文 请求对象。它封装了客户端发出的HTTP请求中的内容
session 请求上下文 用户会话,它的值是字典,解决HTTP协议无法维持回话的情况。
g 应用上下文 处理请求时用作临时存储的对象,每次请求都会重设这个变量
current_app 应用上下文 当前应用的应用实例

Flask在分派请求之前激活应用和请求上下文,该请求处理完成后会将其删除,也就是说在该用户线程运行结束后,会将一些上下文删除。当应用上下文被推送后,就可以在当前线程内使用current_app和g变量。同理当激活请求上下文后当前线程也可以使用request、session请求上下文。

如果不太理解这些概念也没有关系,后续我们会经常使用到。

请求分派

服务端接收到客户端发送的请求时,要找到处理该请求的对应的视图函数,Flask会在URL映射中查找请求的URL。URL映射是URL和视图函数之间的对应关系。Flask使用@app.route()装饰器来构建。

1
2
3
@app.route('/hello')
def index():
return '<h1>42Team</h1>'

/helloURL映射到index()视图函数中。如果你有点忘记了视图函数和路由这些概念你可以回去看看《路由和视图》。完整的路由和视图操作叫做请求分派。

request请求对象

Flask通过请求上下文request来对外开放请求对象。请求对象很有用,它包含了客户端浏览器发送的HTTP全部请求信息。下表中列出所有请求对象常用属性和方法。

属性或方法 返回类型 说明
form 字典 存储请求提交的所有表单字段
args 字典 存储通过URL查询字符串传递的所有参数
values 字典 formargs的合集
cookies 字典 存储请求的所有cookie
headers 字典 存储请求的所有HTTP请求头
files 字典 存储请求上传的所有文件
method 字符串 返回当前HTTP请求方法
is_secure 布尔 是否使用可靠的HTTPS加密连接
endpoint 字符串 返回处理当前请求的视图函数的函数名
scheme 字符串 URL 方案(http 或 https)
host 字符串 请求定义的主机名,如果客户端其定义了端口号,还包括端口号
remote_addr 字符串 客户端的发送HTTP请求的IP地址
path 字符串 URL的路径部分
url 字符串 客户端请求的完成URL
get_data() 返回请求主体缓冲的数据

请求钩子

使用请求钩子可以在处理视图函数前进行额外的操作,可以用于建立数据库连接或者验证用户身份。当然也可以请求结束后执行的额外的操作,比如关闭数据库连接等。

请求钩子 说明
before_request 在每次请求前执行
before_first_request 仅第一次请求时执行
after_request 如果没有未处理的异常,会在请求结束后执行
teardown_request 即使有未处理的异常,也会在请求结束后执行

在钩子函数和视图函数之间共享数据一般使用上下文全局变量g。例如before_request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视图函数时,便可以通过 g.user 获取用户。

响应

Flask在调用视图函数后,会将其返回值作为相应内容。多数情况下,响应就是返回一个HTML页面或者JSON数据。

HTTP协议中不仅仅要有请求响应的字符串,还有一个部分叫做状态码,这个在HTTP协议快速了解章节中提到过。Flask默认正常情况下的状态码是200,表明请求已经被正确的处理。

如果视图函数需要返回一个指定的状态码,那么在字符串后方,加入加入第二个参数。将返回码作为第二个参数返回到页面上。

当然你也可以添加第三个参数,在这里返回你自定义的响应首部,也就是响应头。

1
2
3
@app.route('/return_code')
def return_code():
return 'Where Is Status Code ?', 233 , {'Are you ok ':'Yeah!'}

如果不想每次都在return后面接上这些参数组成的远足你也可以使用Flask视图函数提供的make_response()函数,将上述的三个参数传入进这个函数,等效于我们直接在return后面写。

1
2
3
4
5
6
7
8
from flask import make_response

@app.route('/')
def index():
response = make_response('Where Is Status Code ?', 233 , {'Are you ok ':'Yeah!'})
response.set_cookie('', '42')
return responseanswer

响应头-2020-04-26

常见的响应对象如下表格。

属性或方法 说明
status_code HTTP数字状态码
headers 包含随响应发送的所有首部
content_length 响应主体的长度
set_data() 使用字符串或字节值设定响应
set_cookie() 为响应添加一个cookie
delete_cookie() 删除一个cookie

重定向

有一个特殊的响应叫重定向,这种响应没有页面,但是会告诉浏览器一个新的URL,用来加载新页面。重定向的HTTP状态码通常是302。

可以通过redirect()函数来返回重定向响应。

1
2
3
4
5
from flask import redirect

@app.route('/redirect')
def do_redirect():
return redirect('/hello')

你也可以自己定义重定向,不过既然已经有提供好的函数就不要使用自定义重定向的方式了。

1
2
response.status_code=302
response.headers['Location']='/hello'

返回错误

还有一种特殊的响应由 abort() 函数生成,用于处理错误。下面这里例子中,如果user路径后方参数不是root,那么会返回HTTP500状态码(服务器内部错误)。

1
2
3
4
5
6
7
from flask import abort

@app.route('/user/<user>')
def get_user(user):
if user != 'root':
abort(500)
return '<h1>Hello, {}</h1>'.format(user)

注意abort()的参数必须是异常的状态码。服务器会根据错误码自动生成错误描述。而且一旦使用了abort()那么控制权就不会是调用它的函数了,它会直接抛出相应的异常。