2.2 请求与响应

2.2.1 应用和请求上下文

Flask 从客户端收到请求时,要让视图函数能访问一些对象,才能处理请求。要想让视图函数能访问请求对象,一种直接方式是将其作为参数传入视图函数,不过会导致每个视图函数都会多出一个参数。除了访问请求对象,如果还要访问其他对象,情况便的糟糕。为了避免这种情况, Flask 使用上下文机制临时把某些对象变为全局可访问。

在 Flask 中有两种上下文:应用上下文(application context)和请求上下文(request context)。

Flask 上下文全局变量:

变量名

上下文

说明

current_app

应用上下文

当前应用的应用实例

g

应用上下文

处理请求时用 作临时存储的对象,每次请求都会重设

request

请求上下文

请求对象,封装了客户端发出的 HTTP 请求中的内容

session

请求上下文

用户会话,值为一 个字典,存储请求之间需要“记住”的值

Flask 在分派请求之前激活(或推送)应用和请求上下文,请求处理完成后再将其删除。应用上下文被推送后,就可以在当前线程中使用 current_appg 变量。请求上下文被推送后,就可以使用 requestsession 变量。如果使用这些变量时没有激活应用和请求上下文,就会导致错误。

获取应用上下文的方法是在应用实例上调用 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()