当前位置:网站首页>Flask1.1.4 werkzeug1.0.1 source code analysis: Routing

Flask1.1.4 werkzeug1.0.1 source code analysis: Routing

2022-07-07 20:23:00 An engineer$

Routing is roughly divided into two parts :

  1. Routing binding : take url_rule and view_func Bound to the falsk In the example , This is in the previous article Start process I've already talked about it in
  2. Route resolution : When the actual request comes , according to environ To match to the corresponding Rule, And from environ The parameters are resolved from , And then according to Rule.endpoint Go to view_functions Find the corresponding view_function To call

This article focuses on the second part ,Flask The route matching of is actually based on werkzeug.routing.Map and werkzeug.routing.Rule
We can write a simple demo, Learn how to use these two tools .

#  structure map
url_map = Map([Rule("/a", endpoint='a'), Rule("/b", endpoint='b'), Rule("/c/<path_param>", endpoint='c')])
#  call Map.bind()  return  MapAdapter object 
urls = url_map.bind('eee.com', '/')
# MapAdapter.match()  Make request matching 
print(urls.match("/a"))
print(urls.match("/c/123"))
print(urls.match("/dd"))

give the result as follows :

# When matched, it will return  (endpoint, view_func_args)
('a', {
    })
('c', {
    'path_param': '123'})
# It doesn't match   Throw exceptions 
Traceback (most recent call last):
  File "/Users/panc/Documents/projects/PycharmProjects/grpc-client/pycode/flask_demo.py", line 9, in <module>
    print(urls.match("/dd"))
  File "/usr/local/Caskroom/miniconda/base/envs/python37/lib/python3.7/site-packages/werkzeug/routing.py", line 1945, in match
    raise NotFound()
werkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

ok, Now let's start to study Flask The routing process of ~
First of all, pay attention ,Flask There is no direct URL and view_func Connect , Instead, it adds endpoint The concept of . In fact, it's easy to understand ,URL Most of the time, it is not fixed , It also includes the parameter part , Not suitable for key. in addition , From the steps of processing the request , First, according to the request url Information to match the registered url_rule, And then find the corresponding view_func Perform logical . This is divided into two steps , take url Information and view_func Separate storage , And then through endpoint It seems appropriate to connect the two .

Start process It has been said in an article ,HTTP Format data to WSGI After formatting data , call flask_app(environ, start_response) Execute specific processing logic

class Flask(_PackageBoundObject):
    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)
        
    def wsgi_app(self, environ, start_response):
    	#  Here we build  flask.ctx.RequestContext  object 
    	#  The build process includes   Routing processing logic 
        ctx = self.request_context(environ)
        error = None
        try:
            try:
            	#  Routing logic is also included here   Stack the current request context 
            	#  The purpose of stack entry is to pass some information to the following by non parameter transfer view_function
                ctx.push()
                # RequestContext  Object construction and push The routing logic has been handled in the process of 
                #  After that, you only need to get the currently requested  RequestContext example , use endpoint Just get and call the method 
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:  # noqa: B001
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            #  Clean up the current context after the request processing   Out of the stack 
            ctx.auto_pop(error)

    def request_context(self, environ):
    	#  Notice that the first parameter is flask example 
        return RequestContext(self, environ)

    def create_url_adapter(self, request):
        if request is not None:
            subdomain = (
                (self.url_map.default_subdomain or None)
                if not self.subdomain_matching
                else None
            )
            #  Previous demo I've seen this inside  bind_to_environ  and bind It's the same thing 
            #  Return to one  MapAdapter object 
            return self.url_map.bind_to_environ(
                request.environ,
                server_name=self.config["SERVER_NAME"],
                subdomain=subdomain,
            )

    def full_dispatch_request(self):
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            rv = self.preprocess_request()
            if rv is None:
            	#  Perform the requested 
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        #  Here will be view_function Return   Convert to  Response object 
        return self.finalize_request(rv)

    def dispatch_request(self):
    	#  Take out the current RequestContext object   And get it request attribute 
        req = _request_ctx_stack.top.request
        #  Route matching failed   Throw exceptions 
        if req.routing_exception is not None:
            self.raise_routing_exception(req)
        rule = req.url_rule
        #  Final  Rule Object's endpoint To find the corresponding view_function And call... With parameters   Execute specific logic 
        return self.view_functions[rule.endpoint](**req.view_args)

Now let's look at the key of routing RequestContext

class RequestContext(object):

    def __init__(self, app, environ, request=None, session=None):
    	#  Saved here  flask app example 
        self.app = app
        if request is None:
        	#  Use environ  Construct a  Request object 
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = None
        try:
        	#  here   Is to Map.bind  Got a  MapAdapter object 
            self.url_adapter = app.create_url_adapter(self.request)
        except HTTPException as e:
            self.request.routing_exception = e
        self.flashes = None
        self.session = session

    def match_request(self):
        try:
        	#  In fact, that is  MapAdapter.match()  There is no reference here because it was  Map.bind_to_environ()
            result = self.url_adapter.match(return_rule=True)
            #  unpacking  url_rule namely Rule Object contains endpoint Information  view_args The parameter 
            #  in other words  RequestContext object push() when , The route resolution process has been completed 
            #  Bind the parsing result to  RequestContext Object's request Attribute 
            self.request.url_rule, self.request.view_args = result
        except HTTPException as e:
            self.request.routing_exception = e

    def push(self):
        """Binds the request context to the current context."""
       	#  Stack the current request context object   Convenient for subsequent delivery 
        _request_ctx_stack.push(self)
		
        if self.url_adapter is not None:
        	#  The key 
            self.match_request()

ok, At this point, the route parsing process is over ~
To sum up :

  1. The core of the core is still werkzeug Of Map、Rule、MapAdapter object
  2. The binding of route is flask_app.add_url_rule() There are many ways , It can be app.route() Or use blueprint, But the essence is flask_app.add_url_rule()
  3. Route resolution , Nature is map_adapter = url_map.bind_to_environ() -> rule, view_args = map_adapter.match() -> view_functions[rule.endpoint] (**view_args)
  4. among url_map.bind_to_environ() stay structure RequestContext Object , match Operation in RequestContext.push() When the . After the context object is ready to be merged into the stack , Just take out the current request context object , And then use endpoint To find the corresponding view_func And execute it .
原网站

版权声明
本文为[An engineer$]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207071816154712.html