flask 源码解析:响应
这是 flask 源码解析系列文章的其中一篇,本系列所有文章列表:
response 简介在 flask 应用中,我们只需要编写 view 函数,并不需要直接和响应(response)打交道,flask 会自动生成响应返回给客户端。
我们知道 HTTP 响应分为三个部分: HTTP/1.1?200?OK Access-Control-Allow-Origin:?* Cache-Control:?max-age=600 Content-Encoding:?gzipContent-Type:?text/html;?charset=utf-8 Date:?Wed,?15?Feb?2017?07:50:41?GMT Expires:?Wed,?15?Feb?2017?08:00:41?GMT Last-Modified:?Wed,?15?Feb?2017?07:46:56?GMT Server:?GitHub.com Transfer-Encoding:?chunked X-GitHub-Request-Id:?D2A7:7B6B:33C0628:47C44B9:58A40851<BODY> flask 自然也会提供所有这些数据的操作,视图函数就支持返回三个值:第一个是返回的数据,第二个是状态码,第三个是头部字典。比如: @app.route('/')def?hello_world(): ????return?'Hello,?World!',?201,?{'X-Foo':?'bar'} 这篇文章就讲讲这背后的魔法。 flask 响应(response)在?flask 源码解析:应用启动流程?的最后,我们讲到?
def?finalize_request(self,?rv,?from_error_handler=False): ????"""Given?the?return?value?from?a?view?function?this?finalizes ????the?request?by?converting?it?into?a?response?and?invoking?the ????postprocessing?functions.??This?is?invoked?for?both?normal ????request?dispatching?as?well?as?error?handlers. ????""" ????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 里面有两个方法调用: def?make_response(self,?rv): ????"""Converts?the?return?value?from?a?view?function?to?a?real ????response?object?that?is?an?instance?of?:attr:`response_class`. ????""" ????status_or_headers?=?headers?=?None ????if?isinstance(rv,?tuple): ????????rv,?status_or_headers,?headers?=?rv?+?(None,)?*?(3?-?len(rv)) ????if?isinstance(status_or_headers,?(dict,?list)): ????????headers,?status_or_headers?=?status_or_headers,?None ????if?not?isinstance(rv,?self.response_class): ????????#?When?we?create?a?response?object?directly,?we?let?the?constructor ????????#?set?the?headers?and?status.??We?do?this?because?there?can?be ????????#?some?extra?logic?involved?when?creating?these?objects?with ????????#?specific?values?(like?default?content?type?selection). ????????if?isinstance(rv,?(text_type,?bytes,?bytearray)): ????????????rv?=?self.response_class(rv,?headers=headers,?????????????????????????????????????status=status_or_headers) ????????????headers?=?status_or_headers?=?None ????if?status_or_headers?is?not?None: ????????if?isinstance(status_or_headers,?string_types): ????????????rv.status?=?status_or_headers????????else: ????????????rv.status_code?=?status_or_headers????if?headers: ????????rv.headers.extend(headers) ????return?rv NOTE:因为视图函数可以返回? 不管视图函数返回的是什么,最终都会变成? from?werkzeug.wrappers?import?Response?as?ResponseBaseclass?Response(ResponseBase): ????"""The?response?object?that?is?used?by?default?in?Flask.??Works?like?the ????response?object?from?Werkzeug?but?is?set?to?have?an?HTML?mimetype?by ????default.??Quite?often?you?don't?have?to?create?this?object?yourself?because ????:meth:`~flask.Flask.make_response`?will?take?care?of?that?for?you. ????If?you?want?to?replace?the?response?object?used?you?can?subclass?this?and ????set?:attr:`~flask.Flask.response_class`?to?your?subclass. ????""" ????default_mimetype?=?'text/html' Flask 的?
继续,下面就要分析 werkzeug 对应的代码了。 werkzeug responsewerkzeug 实现的 response 定义在? class?Response(BaseResponse,?ETagResponseMixin,?ResponseStreamMixin,???????????????CommonResponseDescriptorsMixin,???????????????WWWAuthenticateMixin): ????"""Full?featured?response?object?implementing?the?following?mixins: ????-?:class:`ETagResponseMixin`?for?etag?and?cache?control?handling ????-?:class:`ResponseStreamMixin`?to?add?support?for?the?`stream`?property ????-?:class:`CommonResponseDescriptorsMixin`?for?various?HTTP?descriptors ????-?:class:`WWWAuthenticateMixin`?for?HTTP?authentication?support ????""" 和我们在?flask 请求分析的 Request 类一样,这里使用了 Mixin 机制。 class?BaseResponse(object): ????"""Base?response?class.??The?most?important?fact?about?a?response?object ????is?that?it's?a?regular?WSGI?application.??It's?initialized?with?a?couple ????of?response?parameters?(headers,?body,?status?code?etc.)?and?will?start?a ????valid?WSGI?response?when?called?with?the?environ?and?start?response ????callable. ????""" ????charset?=?'utf-8' ????default_status?=?200 ????default_mimetype?=?'text/plain' ????automatically_set_content_length?=?True ????def?__init__(self,?response=None,?status=None,?headers=None,?????????????????mimetype=None,?content_type=None,?direct_passthrough=False): ????????pass
response: 字符串或者其他 iterable 对象,作为响应的 body status: 状态码,可以是整数,也可以是字符串 headers: 响应的头部,可以是个列表,也可以是? mimetype: mimetype 类型,告诉客户端响应 body 的格式,默认是文本格式 content_type: 响应头部的? 所有这些参数都是可选的,默认情况下会生成一个状态码为 200,没有任何 body 的响应。status、status_code 作为? ????def?get_data(self,?as_text=False): ????????"""The?string?representation?of?the?request?body.??Whenever?you?call ????????this?property?the?request?iterable?is?encoded?and?flattened. ????????""" ????????self._ensure_sequence() ????????rv?=?b''.join(self.iter_encoded()) ????????if?as_text: ????????????rv?=?rv.decode(self.charset) ????????return?rv????def?set_data(self,?value): ????????"""Sets?a?new?string?as?response.??The?value?set?must?either?by?a ????????unicode?or?bytestring. ????????""" ????????if?isinstance(value,?text_type): ????????????value?=?value.encode(self.charset) ????????else: ????????????value?=?bytes(value) ????????self.response?=?[value] ????????if?self.automatically_set_content_length: ????????????self.headers['Content-Length']?=?str(len(value)) ????data?=?property(get_data,?set_data,?doc=''' ????????A?descriptor?that?calls?:meth:`get_data`?and?:meth:`set_data`.??This ????????should?not?be?used?and?will?eventually?get?deprecated. ????????''') body 字符的编码和长度都是自动设置的,用户不需要手动处理。 至于头部的存储,werkzeug 使用的是类似于字典的? class?Headers(object): ????"""An?object?that?stores?some?headers.??It?has?a?dict-like?interface ????but?is?ordered?and?can?store?the?same?keys?multiple?times. ????""" ????def?__init__(self,?defaults=None): ????????self._list?=?[] ????????if?defaults?is?not?None: ????????????if?isinstance(defaults,?(list,?Headers)): ????????????????self._list.extend(defaults) ????????????else: ????????????????self.extend(defaults) ????def?__getitem__(self,?key,?_get_mode=False): ????????if?not?_get_mode: ????????????if?isinstance(key,?integer_types): ????????????????return?self._list[key] ????????????elif?isinstance(key,?slice): ????????????????return?self.__class__(self._list[key]) ????????if?not?isinstance(key,?string_types): ????????????raise?exceptions.BadRequestKeyError(key) ????????ikey?=?key.lower() ????????for?k,?v?in?self._list: ????????????if?k.lower()?==?ikey: ????????????????return?v????????if?_get_mode: ????????????raise?KeyError() ????????raise?exceptions.BadRequestKeyError(key) 可以看到,头部信息在内部存储为二元组构成的列表,这样就能同时保证它的有序性和重复性。一个核心的方法是?
然后实现? 自定义 response如果需要扩展 flask? from?flask?import?Flask,?Responseclass?MyResponse(Response): ????passapp?=?Flask(__name__)app.response_class?=?MyResponse 更多可能的用法,可以参考文章末尾的参考资料。 参考资料
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |