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:
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:
- 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.
- Use app.add_url_rule ().
Before you start reading the source code, I have a few questions of this?
- What is the process of registering routing?
- Flask is how the internal management of URL rules?
- How to view a function to bind multiple internal URL is implemented?
- How dynamic URL is a view function to match it?
- 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:
- rule: url rule
- endpoint: To register rules endpoint, the default is the name of the child view function
- view_func: view function
- provide_automatic_options: request method whether to add a flag OPTIONS method
- options: request processing methods and the like on
Can be seen, add_url_rule () first process parameter, comprising:
- endpoint default view function name
- The default method is GET request url
- 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:
- 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
- RequestContext Object push the stack _request_ctx_stack
- 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
- 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.