flask 启动函数返回值的剖析
2.启动函数返回值的剖析
本次剖析根据 flask 内部的处理流程将返回值推导出。
2.1 源码剖析
from werkzeug import run_simple def func(environ,start_response): # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。 print("请求来了") return ????? # return 的返回值是什么呢 if __name__ == '__main__': # app.run() run_simple("127.0.0.1",5000,func) >>> 点击请求地址(请求过来时) >>> 请求来了 >>> 报错 # 函数被执行相当于 func()
flask 的启动函数执行的是__call__()
方法,该方法的源码为
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any: """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app`, which can be wrapped to apply middleware. """ return self.wsgi_app(environ, start_response)
此处调用相关的 wsgi_app(...)
方法,该方法的源码如下
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: ctx = self.request_context(environ) error: t.Optional[BaseException] = None try: try: ctx.push() response = self.full_dispatch_request() # 创建response返回值对象。 except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise # 这里最终返回的是一个 response 对象,response 来源于上方的 full_dispatch_request函数。 return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
根据相关的注释信息去查看相关函数的full_dispatch_request
函数的源码。
def full_dispatch_request(self) -> Response: # 此函数的返回值类型是Response """Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling. .. versionadded:: 0.7 """ self.try_trigger_before_first_request_functions() try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv) # 返回值执行的函数。rv 表示了返回值中的具体内容例如一个字符串
知识补充:->
常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数的返回类型,也可以理解为给函数添加注解。
通俗理解:就是表明了本函数的参数类型和返回值的类型。
参考文献:https://blog.csdn.net/mahoon411/article/details/125363646
finalize_request
函数源码,如下所示:
def finalize_request( self, rv: t.Union[ft.ResponseReturnValue, HTTPException], from_error_handler: bool = False, ) -> Response: # 描述了相关的返回值。 """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. Because this means that it might be called as a result of a failure a special safe mode is available which can be enabled with the `from_error_handler` flag. If enabled, failures in response processing will be logged and otherwise ignored. :internal: """ response = self.make_response(rv) # 其中 rv 表示返回值的具体内容,此函数创建response对象 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 #返回的是一个 response 对象,由上述的 make_response 进行返回。
make_response()
函数的返回值如下,本函数内部较长将删除一些源码注释,提高观察效率,如有需要可以在源码中进行观看:
def make_response(self, rv: ft.ResponseReturnValue) -> Response: status = headers = None # unpack tuple returns if isinstance(rv, tuple): # 检查 rv 是否为相关的类型 len_rv = len(rv) # a 3-tuple is unpacked directly if len_rv == 3: rv, status, headers = rv # type: ignore[misc] # decide if a 2-tuple has status or headers elif len_rv == 2: if isinstance(rv[1], (Headers, dict, tuple, list)): rv, headers = rv else: rv, status = rv # type: ignore[assignment,misc] # other sized tuples are not allowed else: raise TypeError( "The view function did not return a valid response tuple." " The tuple must have the form (body, status, headers)," " (body, status), or (body, headers)." ) # the body must not be None if rv is None: raise TypeError( f"The view function for {request.endpoint!r} did not" " return a valid response. The function either returned" " None or ended without a return statement." ) # make sure the body is an instance of the response class if not isinstance(rv, self.response_class): if isinstance(rv, (str, 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, # type: ignore[arg-type] ) 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 # type: ignore[arg-type] ) except TypeError as e: raise TypeError( f"{e}\nThe view function did not return a valid" " response. The return type must be a string," " dict, tuple, Response instance, or WSGI" f" callable, but it was a {type(rv).__name__}." ).with_traceback(sys.exc_info()[2]) from None 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" f" callable, but it was a {type(rv).__name__}." ) ''' 重点:以上的结构为三个大的if 判断结构,判断rv是否为相关的类型。 ''' rv = t.cast(Response, rv) # 此处是将rv 转换为对象的 Response 的对象。 # prefer the status if it was provided if status is not None: if isinstance(status, (str, bytes, bytearray)): rv.status = status else: rv.status_code = status # extend existing headers with provided headers if headers: rv.headers.update(headers) # type: ignore[arg-type] return rv
cast()
函数的源码如图所示
def cast(typ, val): """Cast a value to a type. This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don't check anything (we want this to be as fast as possible). """ # 通过注释可以发现,这个函数的功能就是转换数据到执行的类型 return val
rv = t.cast(Response, rv)
此处是将rv 转换为对象的 Response 的对象。通过源码可知,第一个参数为对象的类型。查看对象类型源码如下:
from werkzeug.wrappers import Response as ResponseBase # 源码内部的导包 class Response(ResponseBase): #该类继承于 ResponeBase 类。即Response """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. .. versionchanged:: 1.0 JSON support is added to the response, like the request. This is useful when testing to get the test client response data as JSON. .. versionchanged:: 1.0 Added :attr:`max_cookie_size`. """ default_mimetype = "text/html" json_module = json autocorrect_location_header = False @property def max_cookie_size(self) -> int: # type: ignore """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. See :attr:`~werkzeug.wrappers.Response.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().max_cookie_size
通过源码的注释,可以发现这是一个 flask 的内置类。可以直接使用,通过 pycharm
的路径显示(该类在wrappers.py
文件下,改文件在flask 文件夹下【源码文件】。)我们也可以得出他的导包路径为
from flask.wrappers import Response
而此类则是继承于Response
类(源码内部到包时修改为Response
),该类的源码如下图所示:
class Response(_SansIOResponse): pass # 该类的内容太长,不在此处粘贴。
该类是werkzug
下的类,源码文件位于werkzug
文件夹下的 warppers
文件夹下的response
文件中,因此导包路径为
from werkzeug.wrappers.response import Response
至此,可得 flask 中请求的核心功能大多都是有Werkzug
包内部完成的。
2.2 返回值的示例
2.2.1 依赖flask
根据 wsgi_app()
中的源码可知,response对象创建后需要将参数environ, start_response
再进行传入。
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: ctx = self.request_context(environ) error: t.Optional[BaseException] = None try: try: ctx.push() response = self.full_dispatch_request() # 创建response返回值对象。 except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise # 这里最终返回的是一个 response 对象,response 来源于上方的 full_dispatch_request函数。 return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
根据上述流程,将代码进行改造
from werkzeug import run_simple from flask.wrappers import Response def func(environ, start_response): # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。 print("请求来了") response = Response("Hello World") # 创建相关的Response return response(environ,start_response) # 封装参数进行返回 if __name__ == '__main__': # app.run() run_simple("127.0.0.1", 5000, func) >>> # 正常在浏览器中响应,正常返回 >>> * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) >>> 请求来了 >>> 127.0.0.1 - - [21/Jul/2022 10:09:09] "GET / HTTP/1.1" 200 - >>> 请求来了 >>> 127.0.0.1 - - [21/Jul/2022 10:09:09] "GET /favicon.ico HTTP/1.1" 200 -
- 此方法,依赖于flask中的值,而 flask 的请求核心都是依赖于
werkzug
,说明此处可以用相关的父类进行替代。
2.2.2 原生实现
from werkzeug import run_simple from werkzeug.wrappers.response import Response # 导入的是werkzug 包 def func(environ, start_response): # 该函数必须要加上参数:environ,和start_response;否则报错参数异常,因为要满足包内部函数的调用。 print("请求来了") response = Response("Hello World") return response(environ,start_response) # return 的返回值是什么呢 if __name__ == '__main__': # app.run() run_simple("127.0.0.1", 5000, func) >>> # 正常在浏览器中响应,正常返回 >>> * Running on http://127.0.0.1:5000 (Press CTRL+C to quit) >>> 127.0.0.1 - - [21/Jul/2022 10:16:40] "GET / HTTP/1.1" 200 - >>> 请求来了 >>> 请求来了 >>> 127.0.0.1 - - [21/Jul/2022 10:16:40] "GET /favicon.ico HTTP/1.1" 200 -
- 总结:
flask
的网络请求核心全部都是由werkzug
进行的合成。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构