Flask source code analysis II: internal routing implementation principle

Foreword

Flask is by far my favorite a Python Web framework, in order to better achieve its internal control mechanism, this two-day ready to learn at the source Flask, the Deep and share with you the next, which Flask version 1.1.1 .

Last understanding of the startup process Flask services, we look at the internal routing mechanism to achieve today.

Flask series:

  1. On the Development Flask
  2. Flask a source code analysis: service starts

About Routing

The so-called routing, is to deal with the relationship between the URL request procedures and functions.

Flask is also unified management rules on the URL, the URL is created rule in two ways:

  1. Use @ app.route decorator, passing URL rule as an argument, binds a function to the URL, put the process registered as a function of routing, this function is called the view function.
  2. Use app.add_url_rule ().

Before you start reading the source code, I have a few questions of this?

  1. What is the process of registering routing?
  2. Flask is how the internal management of URL rules?
  3. How to view a function to bind multiple internal URL is implemented?
  4. How dynamic URL is a view function to match it?
  5. Routing is the process of matching what is it?

Let us take this with a few questions to learn the source of it!

text

Registration route

First, route () decorator:

def route(self, rule, **options):       

        def decorator(f):
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f

        return decorator

route () has two parameters, rule represents url rule. After this processing parameter function, calling the method add_url_role (), where it is equivalent to two kinds of methods to verify the registered routes. We look at the code:

def add_url_rule(
        self,
        rule,
        endpoint=None,
        view_func=None,
        provide_automatic_options=None,
        **options
    ):
        
        if endpoint is None:
            endpoint = _endpoint_from_view_func(view_func)
        options["endpoint"] = endpoint
        methods = options.pop("methods", None)

        # if the methods are not given and the view_func object knows its
        # methods we can use that instead.  If neither exists, we go with
        # a tuple of only ``GET`` as default.
        if methods is None:
            methods = getattr(view_func, "methods", None) or ("GET",)
        if isinstance(methods, string_types):
            raise TypeError(
                "Allowed methods have to be iterables of strings, "
                'for example: @app.route(..., methods=["POST"])'
            )
        methods = set(item.upper() for item in methods)

        # Methods that should always be added
        required_methods = set(getattr(view_func, "required_methods", ()))

        # starting with Flask 0.8 the view_func object can disable and
        # force-enable the automatic options handling.
        if provide_automatic_options is None:
            provide_automatic_options = getattr(
                view_func, "provide_automatic_options", None
            )

        if provide_automatic_options is None:
            if "OPTIONS" not in methods:
                provide_automatic_options = True
                required_methods.add("OPTIONS")
            else:
                provide_automatic_options = False

        # Add the required methods now.
        methods |= required_methods

        rule = self.url_rule_class(rule, methods=methods, **options)
        rule.provide_automatic_options = provide_automatic_options

        self.url_map.add(rule)
        if view_func is not None:
            old_func = self.view_functions.get(endpoint)
            if old_func is not None and old_func != view_func:
                raise AssertionError(
                    "View function mapping is overwriting an "
                    "existing endpoint function: %s" % endpoint
                )
            self.view_functions[endpoint] = view_func

The parameters include:

  1. rule: url rule
  2. endpoint: To register rules endpoint, the default is the name of the child view function
  3. view_func: view function
  4. provide_automatic_options: request method whether to add a flag OPTIONS method
  5. options: request processing methods and the like on

Can be seen, add_url_rule () first process parameter, comprising:

  1. endpoint default view function name
  2. The default method is GET request url
  3. If the request is not provided OPTIONS method, the adding method.

After all arguments have been processed, the URL write rules url_map (Rule object is created and added to the Map object), the view function writes view_function dictionary.

Wherein, url_map is the werkzeug.routing:Map object class, a rule werkzeug.routing:Rule object class, i.e. the core Flask routing logic is implemented in the werkzeug .

Tool

werkzeug is written in Python WSGI a tool set, werkzeug.routing url parsing module is mainly used.

Rule class

Rule class inherits from RuleFactory class, a Rule instance represents a URL pattern, a WSGI application will deal with many different URL patterns, at the same time produce a number of Rule instances that will be passed as a parameter to the Map class.

Map class

Examples of class Map store all configuration rules url, parse and view function corresponding to the request matches.

Routing match

In the application initialization process, will register all the routing rules, you can call (app.url_map) view, when the service receives a URL request, you need to be routed to match, to find the corresponding view function, corresponding processes and principles are what?

When a user enters a request Flask application, the method calls wsgi_app class Flask:

def wsgi_app(self, environ, start_response):
    
    ctx = self.request_context(environ)
    error = None
    try:
        try:
            ctx.push()
            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
        ctx.auto_pop(error)

The processing function comprising:

  1. RequestContext create objects, call the process object initialization app.create_url_adapter () method, the request is passed to parameters environ Map object created MapAdapter objects stored in url_adapter field
  2. RequestContext Object push the stack _request_ctx_stack
  3. By match_request method RequestContext, call the object's match MapAdapter way to find the matching Rule and parse the parameters stored in the request and view_args field of url_rule
  4. Call full_dispatch_request ()

Next we look full_dispatch_request method:

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:
            rv = self.dispatch_request()
    except Exception as e:
        rv = self.handle_user_exception(e)
    return self.finalize_request(rv)

You can see, the focus of the implementation of dispatch_request ():

def dispatch_request(self):
    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)
        and req.method == "OPTIONS"
    ):
        return self.make_default_options_response()
    # otherwise dispatch to the handler for that endpoint
    return self.view_functions[rule.endpoint](**req.view_args)

Treatment process are: acquiring request of the requested object, find the corresponding Endpoint, in turn, finds the corresponding functions from the view view_functions, the delivery request parameters and view internal logic function returns the processing to complete a distribution request.

The above is the realization of the principle of internal routing Flask.

Guess you like

Origin www.cnblogs.com/ybjourney/p/11789983.html