0%

flask源码之Response对象(七)

原理

image-20210115225826137

实现

full_dispatch_request方法

image-20210115230023316

回到wsgi_app方法

image-20210115230235510

回顾一下wsgi协议

你要被wsgi server调用,那么你的返回值,应该是可以被迭代的,例如下面的list

1
2
3
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']

也就是在flask里面,这个Response对象,执行 __call__方法之后返回的是一个类似 list的可迭代对象

我们知道既然Response对象是被finalize_request方法返回的,那他的实例化过程应该就在这个方法中

我们找到 finalize_request方法

1
2
3
4
5
6
7
8
9
10
11
12
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv)
try:
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
return response

再看 make_response,代码有点多

这个函数的大概逻辑就是,你的rv是视图函数的返回值

如果返回值本身就是 Response 实例,就直接使用它;如果返回值是字符串类型,就把它作为响应的 body,并自动设置状态码和头部信息;
如果返回值是 tuple,会尝试用 (response, status, headers) 或者 (response, headers) 去解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def make_response(self, rv):

# make sure the body is an instance of the response class
# 看这里就好了,
if not isinstance(rv, self.response_class):
if isinstance(rv, (text_type, bytes, bytearray)):
# let the response class set the status and headers instead of
# waiting to do it manually, so that the class can handle any
# special logic
rv = self.response_class(rv, status=status, headers=headers)
status = headers = None
elif isinstance(rv, dict):
rv = jsonify(rv)
elif isinstance(rv, BaseResponse) or callable(rv):
# evaluate a WSGI callable, or coerce a different response
# class to the correct type
try:
rv = self.response_class.force_type(rv, request.environ)
except TypeError as e:
new_error = TypeError(
"{e}\nThe view function did not return a valid"
" response. The return type must be a string, dict, tuple,"
" Response instance, or WSGI callable, but it was a"
" {rv.__class__.__name__}.".format(e=e, rv=rv)
)
reraise(TypeError, new_error, sys.exc_info()[2])
else:
raise TypeError(
"The view function did not return a valid"
" response. The return type must be a string, dict, tuple,"
" Response instance, or WSGI callable, but it was a"
" {rv.__class__.__name__}.".format(rv=rv)
)

# prefer the status if it was provided
if status is not None:
if isinstance(status, (text_type, bytes, bytearray)):
rv.status = status
else:
rv.status_code = status

# extend existing headers with provided headers
if headers:
rv.headers.extend(headers)

return rv

我们还可以看出来,Response对象就是 response_class的实例,点进去就可以看到定义了

Response定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Response(ResponseBase, JSONMixin):
default_mimetype = "text/html"

def _get_data_for_json(self, cache):
return self.get_data()

@property
def max_cookie_size(self):
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.

See :attr:`~werkzeug.wrappers.BaseResponse.max_cookie_size` in
Werkzeug's docs.
"""
if current_app:
return current_app.config["MAX_COOKIE_SIZE"]

# return Werkzeug's default when not in an app context
return super(Response, self).max_cookie_size

又是mixin实现多继承

别忘了,我们的目的是看他的 __call__方法返回的是否是迭代对象

不用大海捞针去父类找,pycharm已经帮我们找好了,在左侧structure中找

image-20210115231514061

1
2
3
4
5
6
7
8
9
10
11
def __call__(self, environ, start_response):
"""Process this response as WSGI application.

:param environ: the WSGI environment.
:param start_response: the response callable provided by the WSGI
server.
:return: an application iterator
"""
app_iter, status, headers = self.get_wsgi_response(environ)
start_response(status, headers)
return app_iter

看名字也知道,app_iter是可迭代对象,可以被wsgi server迭代调用

start_responsewsgi server提供的回调函数,我们调用就好了,wsgi server会帮我们返回http response

再看一眼 wsgi协议的demo

1
2
3
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']

可以发现 header的格式要是listtuple这种

werkzeug实现了Headers数据结构

定义在

1
fro werkzeug.datastructures import Headers

他是一个类似字典的数据结构,只不过支持多个相同的key以及key的有序

参考Headers对象

自定义Response对象

我们知道Reseponse对象是在 make_response 种这样被实例化的

1
rv = self.response_class(rv, status=status, headers=headers)

因此我们只需要

1
2
3
4
5
6
7
from flask import Flask, Response

class MyResponse(Response):
pass

app = Flask(__name__)
app.response_class = MyResponse

便可以定义我们自己的Response对象