Website has been written for a long time, and always want to analyze and analyze the operation of the flask, but the source is intermittent, but the recent state is good, the progress of a point, here to create a new category, specifically to say flask and the source of the content of the relationship, this preparation to roughly say the application of flask framework, The process of starting from an HTTP request to a response
Front-facing skill---WSGI
Before the specific reading source, here first need to say a concept, what is WSGI.
WSGI, full name Web server gateway Interface, or Python Web server gateway Interface, is a simple and easy-to-pass between a Web server and a Web application or framework defined for the Python language The interface used. Similar interfaces have appeared in many other languages since WSGI was developed.
The official definition of WSGI is the Python Web Server Gateway Interface. From the name it can be seen that this thing is a gateway, that is, gateways. The purpose of a gateway is to convert between protocols.
WSGI is a low-level interface between a Web server and a Web application or application framework to enhance the common denominator of portable web application development. WSGI is designed based on the existing CGI standards.
Many frameworks have their own WSGI servers, such as Flask,webpy,django, CherryPy, and so on. Of course, the performance is not good, the Web server comes with more testing purposes, the release of the use of the production environment of the WSGI server or the joint nginx do UWSGI.
People who have searched the Wsgi on the Internet should see a graph, the server on the left, the app on the right, and a link in the middle is wsgi.
However, I read the source code after the understanding and this is somewhat different, I personally think, actually should not write an app alone, because this wsgi is actually included in the app, the right side of the app should actually refer to the logic function, including the URL and view The correspondence between function.
So I personally understand the following picture of myself
Wsgi actually acts as an interface to accept the information passed by the server, and then invokes the view function in the background app to respond through this interface.
Wsgi specific functions
The above mentioned WSGI can play an interface function, front docking server, back docking app specific functions
Let's take a look at one of the simplest WSGI_APP implementations.
def application (environ, start_response): #一个符合wsgi协议的应用程序写法应该接受2个参数 start_response (' OK ', [(' Content-type ', ' text/html ')]) #environ为http的相关信息, such as the request for first-class start_response is the response message return [b '
However, as the app itself, even if you start the program, you can not pass parameters to application?
So, in fact, the invocation of application and the passing of 2 parameters is done by the server, such as Gunicorn.
And this is called application, in the Flask framework inside the name, called Wsgi_app, please see the following section of the source code.
Flask and Wsgi generating flask instancesLet's take a look at the operating instructions for generating flask applications, which are definitely familiar to people who have used them.
From flask Import Flaskapp = Flask (__name__) #生成app实例 @app. Route ("/") def index (): return ' Hello world '
In this way, a flask app generates a
But here is a concept must be clear, that is, when your Gunicorn received an HTTP request to call the app, he actually used the Flask __call__ method, this is very important!!!
Because how the __call__ method is written determines where your entire process begins.
Let's look at the source code for the __call__ method of flask class.
Class Flask (_packageboundobject): #Flask类 # middle omit some code def __call__ (self, Environ, start_response): # __call__ method for flask instance "" "Shortcut for:attr: ' Wsgi_app '. " "" Return Self.wsgi_app (environ, start_response) #注意他的return, when he returns, is actually called Wsgi_app this function
As a result, we know that when the HTTP request is sent from the server, he will start the __call__ function, and finally actually call the Wsgi_app function and Pass in Environ and start_response
Wsgi_app Definition of flaskClass Flask (_packageboundobject): #中间省略一些代码 #请注意函数的说明, very accurate, this wsgi_app is a true WSGI application Def wsgi_app (self, Enviro N, start_response): #他扮演的是一个中间角色 "" "The actual WSGI application. This isn't implemented in ' __call__ ' So, middlewares can be applied without losing a reference to the C Lass. So instead of doing this:: App = Mymiddleware (APP) It's a better idea to doing this instead:: AP P.wsgi_app = Mymiddleware (App.wsgi_app) then you still has the original application object around and can C Ontinue to call methods on it. :p Aram environ:a WSGI Environment:p Aram START_RESPONSE:A Callable accepting a status code, A list of headers and an optional exception context to start the response "" "CTX = Self.request_context (environ) ctx.push () error = None Try:try: Response = Self.full_Dispatch_request () #full_dispatch_request起到了预处理和错误处理以及分发请求的作用 except Exception as E:error = E response = Self.make_response (Self.handle_exception (e)) #如果有错误发生, generates an error response return response (ENVI Ron, Start_response) #如果没有错误发生, then responds to the request normally, returning the response content Finally:if Self.should_ignore_error (Error): Error = None Ctx.auto_pop (Error)
Ok, the function definition of this wsgi_app, basically contains the function of the whole process
Internal flow of Wsgi_app
First step: Generate request Requests object and request context environmentFirst, you will see the statement of CTX = Self.request_context (environ), which involves flask using the concept of the request context and the application context , the structure of the stack structure , This part is more difficult, the second post will be written separately.
It is only necessary to understand that the above statement is generated using a Request object and a request context containing the requested information.
Part Two: The process of requesting access to preprocessing, error handling, and request forwarding to the responseInside the function of Wsgi_app, after generating the request object and the context, enter into the try
Response = Self.full_dispatch_request ()
We see that the response is assigned to the return content of the Full_dispatch_request () method, so let's take a look at the full_dispatch_request method
Class Flask (_packageboundobject): #此处省略一些代码 def full_dispatch_request (self): "" "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) #socket部分的操作 RV = self.preprocess_request () #进行请求的预处理 if RV is None: RV = self.dispatch_request () except Exception as e: RV = self.handle_user_exception (e) Response = Self.make_response (RV) response = self.process_response (response) request_finished.send (self, Response=response) return response
He will first trigger the Try_trigger_before_first_request_function () method
Inside the method----------> triggers the _got_first_request property, which returns a value of True or False. If True, the program begins processing the request .
Take a look at the code for Try_trigger_before_first_request_function (), whose purpose is to finally set the _got_first_request property to True.
Class Flask (_packageboundobject): #省略一些代码 def try_trigger_before_first_request_functions (self): "" " called Before each request and would ensure the IT triggers the:attr: ' Before_first_request_funcs ' and only exactly once per< C3/>application instance (which means process usually). : internal: "" " if self._got_first_request: Return with self._before_request_lock: if self._got_first_request: return to func in Self.before _first_request_funcs: func () self._got_first_request = True
And then look at the definition of _got_first_request, his default value is False
His definition can be seen clearly, if the application Started,this attribute is set to True.
Class Flask (_packageboundobject): #省略一些代码 @property def got_first_request (self): ' "" This attribute is Set to ' True ' if the application started handling the first request ... versionadded:: 0.8 "" " return SE Lf._got_first_request
Then, when the _got_first_request property is set, we need to go back inside the full_dispatch_request function and continue down.
The following section of code is request_started.send (), he is inherited from the signal module, the general role is to carry out the socket part of the function, temporarily not detailed retrospective.
Preprocess_request () method, the main is to carry out flask hook hooks, before_request function implementation, that is, before the actual request, some things need to be done in advance
There are 4 hooks in flask, and write it again.
Then, continuing down, came a vital function dispatch_request ()
Try: request_started.send (self) RV = self.preprocess_request () if RV is None: RV = Self.dispatch_ Request ()
Why is it important, because an HTTP request is here, actually has completed the transition from the WSGI part, into the search for a response phase, a request through the URL came in, the app how to know how to respond?
is to use the Dispatch_request method to determine and distribute the request.
Step three: Request distribution Dispatch_requestLook at the source
Class Flask (_packageboundobject): #省略一些代码 def dispatch_request (self): #看函数定义, matches the URL and returns the value of The view or error. "" "Does the request dispatching. Matches the URL and returns the return value of the the view or error handler. This does not has a to is a response object. In order to convert the return value to a proper response object, Call:func: ' Make_response '. .. Versionchanged:: 0.7 This no longer does the exception handling, this code is moved to the New:meth: ' Full_dispatch_request '. "" "req = _request_ctx_stack.top.request if req.routing_exception is not None:self.raise_routing _exception (req) rule = req.url_rule # If we provide automatic options for this URL and the # request Came with the OPTIONS method, reply automatically if getattr (rule, ' provide_automatic_options ', False) a nd Req.method = = ' OPTIONS ': Return self.mAke_default_options_response () # Otherwise dispatch to the handler for that endpoint return SELF.VIEW_FUNCTI Ons[rule.endpoint] (**req.view_args) #最终进入view_functions, take out the return value of the view function corresponding to the URL
The middle does not need too much consideration, req = _request_ctx_stack.top.request can be temporarily understood as, the request object is assigned to req
Here is a brief, after each URL comes in, he will correspond to a view_function
For example, the following is a simple view function, the path '/' corresponds to the index function
However, in fact, it is divided into 2 steps, the first step is '/' corresponding to the endpoint ' index ', the second is Endpoint ' index ' corresponding to the index () view function
This is also put in the second article specifically write flask routing implementation , here can temporarily ignore the intermediate step, as long as the URL----------->view function logical step is OK
@app. Route ('/') def index (): return ' Hello world '
In addition, View_functions is a dictionary form , and the relationship between his key and value is endpoint------> View function
So every valid URL comes in and finds his corresponding view function, gets the return value and assigns the value to RV
Like the simple index above, he gets the ' Hello world ' value
After the request distribution is complete, the returned value has been obtained, and then the next step is how to do
We're back in the Full_dispatch_request method.
Response = Self.make_response (RV) response = self.process_response (response) request_finished.send (self, Response=response) return response
At this point, the RV generated response, which was just obtained, is re-assigned through the Make_response function response
Again through the Process_response function is mainly to deal with a after_request function, such as you after the request, the database connection to close the action, and the above mentioned before_request correspondence and similar.
After the processing of Request_finished.send, is also related to socket processing, temporarily not detailed in-depth.
The new response object is then returned
In particular, it is important to note that the Make_response function is a very significant function, and his role is to return an Response_class instance object, that is, an object that can accept Environ and start_reponse two parameters.
Very IMPORTANT!!!
Converts the return value from a view function to a real response object this is an instance of:attr: ' Response_class
Class Flask (_packageboundobject): #注意函数说明, converts the return value from view function to a real response object #省略一 Section Code def make_response (self, RV): "" "Converts the return value from a view function to a real response ob Ject that's an instance of:attr: ' Response_class '. The following types is allowed for ' RV ':. Tabularcolumns:: |p{3.5cm}|p{9.5cm}| ======================= ===========================================: attr: ' Response_class ' The object is returned Unchanged:class: ' str ' A response object is created with the string as Bo Dy:class: ' Unicode ' a response object is created with the string encoded to U Tf-8 as body a WSGI function The function is called as WSGI application and Buffered as response Object:class: ' tuple ' A tuple in the form ' (response, status, Headers) "or" (response, headers) ' where ' response ' is any of the Types defined here, ' status ' was a string or an integer and ' head ERs ' is a list or a dictionary with header values. ======================= ===========================================:p Aram rv:the return value from the view funct Ion.. Versionchanged:: 0.9 Previously a tuple is interpreted as the arguments for the response object. "" "status_or_headers = headers = None if Isinstance (rv, tuple): RV, Status_or_headers, Heade rs = rv + (None,) * (3-len (RV)) If RV is None:raise valueerror (' View function does not return a respon Se ') if isinstance (Status_or_headers, (dict, List)): headers, status_or_headers = Status_or_headers, Non E If not isinstance (RV, Self.response_class): # When we create a response object directly, we let the constructor # set the headers and S Tatus. We do this because there can is # some extra logic involved when creating these objects with # Speci FIC 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 Else: RV = Self.response_class.force_type (rv, Request.environ) 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
Fourth step: Return to Wsgi_app InteriorFinally the final step, the process went back to the interior of the Wsgi_app
The following is the code inside the Wsgi_app
Try: try: response = self.full_dispatch_request () except Exception as e: error = e response = Self.make_response (Self.handle_exception (e)) return response (environ, start_response) finally: if Self.should_ignore_error (Error): error = None ctx.auto_pop (Error)
When response returns from the Full_dispatch_request function, the function adds Environ, start_response parameters to the response and returns to Gunicorn
At this point, an HTTP request-to-response process is complete.
In general, the key steps of a process can be simply summed up as follows:
Later, will record the flask of the route implementation, the URL of how to correspond with endpoint, endpoint and view function is how to correspond together
This article:
Http://docs.jinkan.org/docs/flask/api.html?highlight=wsgi_app#flask.Flask.wsgi_app
1190000004223296
Http://docs.gunicorn.org/en/stable/run.html
Flask Interpretation---Elementary introduction to flask basic Work Flow _1