CherryPy 请求处理流程
后端应用使用 Tree
实例的 mount
方法将用户对象封装成一个 Application
实例挂载到路由处理链上:
# cherrypy/__init__.py
tree = _cptree.Tree()
cherrypy/_cptree.py
class Tree(object):
apps = {}
def __init__(self):
self.apps = {}
def mount(self, root, script_name="", config=None):
# Next line both 1) strips trailing slash and 2) maps "/" -> "".
script_name = script_name.rstrip("/")
app = Application(root, script_name)
self.apps[script_name] = app
return app
def script_name(self, path=None):
while True:
if path in self.apps:
return path
if path == "":
return None
# Move one node up the tree and try again.
path = path[:path.rfind("/")]
def __call__(self, environ, start_response):
sn = self.script_name(path or "/")
if sn is None:
start_response('404 Not Found', [])
return []
app = self.apps[sn]
return app(environ, start_response)
class Application(object):
wsgiapp = None
request_class = _cprequest.Request
response_class = _cprequest.Response
def __init__(self, root, script_name="", config=None):
self.wsgiapp = _cpwsgi.CPWSGIApp(self)
def get_serving(self, local, remote, scheme, sproto):
"""Create and return a Request and Response object."""
req = self.request_class(local, remote, scheme, sproto)
req.app = self
resp = self.response_class()
cherrypy.serving.load(req, resp)
return req, resp
def release_serving(self):
"""Release the current serving (request and response)."""
req = cherrypy.serving.request
req.close()
cherrypy.serving.clear()
def __call__(self, environ, start_response):
return self.wsgiapp(environ, start_response)
请求经由各中间件处理,然后返回 AppResponse
实例作为响应:
# cherrypy/_cpwsgi.py
class CPWSGIApp(object):
"""A WSGI application object for a CherryPy Application."""
pipeline = [('ExceptionTrapper', ExceptionTrapper),
('InternalRedirector', InternalRedirector),
]
"""A list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
constructor that takes an initial, positional 'nextapp' argument,
plus optional keyword arguments, and returns a WSGI application
(that takes environ and start_response arguments). The 'name' can
be any you choose, and will correspond to keys in self.config."""
head = None
"""Rather than nest all apps in the pipeline on each call, it's only
done the first time, and the result is memoized into self.head. Set
this to None again if you change self.pipeline after calling self."""
response_class = AppResponse
"""The class to instantiate and return as the next app in the WSGI chain."""
def __init__(self, cpapp, pipeline=None):
self.cpapp = cpapp
self.pipeline = self.pipeline[:]
if pipeline:
self.pipeline.extend(pipeline)
def tail(self, environ, start_response):
"""WSGI application callable for the actual CherryPy application.
any WSGI middleware in self.pipeline runs first.
"""
return self.response_class(environ, start_response, self.cpapp)
def __call__(self, environ, start_response):
head = self.head
if head is None:
# Create and nest the WSGI apps in our pipeline (in reverse order).
# Then memoize the result in self.head.
head = self.tail
for name, callable in self.pipeline[::-1]:
conf = self.config.get(name, {})
head = callable(head, **conf)
self.head = head
return head(environ, start_response)
class AppResponse(object):
"""WSGI response iterable for CherryPy applications."""
def __init__(self, environ, start_response, cpapp):
self.cpapp = cpapp
self.run()
r = _cherrypy.serving.response
self.write = start_response(outstatus, outheaders)
def run(self):
"""Create a Request object using environ."""
request, resp = self.cpapp.get_serving(local, remote, scheme, sproto)
request.run(meth, path, qs, rproto, headers, rfile)
AppResponse
实例的 run
方法调用 Request
实例的 run
方法并最终调用用户对象 expose
出来的方法得到后端接口的用户数据:
# cherrypy/_cprequest.py
class Request(object):
handler = None
"""
The function, method, or other callable which CherryPy will call to
produce the response. The discovery of the handler and the arguments
it will receive are determined by the request.dispatch object.
By default, the handler is discovered by walking a tree of objects
starting at request.app.root, and is then passed all HTTP params
(from the query string and POST body) as keyword arguments."""
def run(self, method, path, query_string, req_protocol, headers, rfile):
self.handler = None
self.respond(pi)
def respond(self, path_info):
"""Generate a response for the resource at self.path_info. (Core)"""
response = cherrypy.serving.response
self.get_resource(path_info)
response.body = self.handler()
最后修改于 2019-05-30