大家可以使用pycharm,根据这偏文章来看
django版本: 2.2
上节回顾
上节说到在启动 WSGIServer 的时候,需要获取一个handler
,如何加载中间件以及将中间件的对象保存到一个叫_middleware_chain
的变量中, 最后启动 WSGIServer 后进入httpd.server_forever()
的死循环。
本节内容
首先我们看一下 WSGIServer 类的继承关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAwp1nmV-1598856705330)(./1598599363601.png)]
了解了 WSGIServer 的继承关系后,我们来看代码:在 httpd.server_forever() 死循环中,当有请求过来的时候,就会调用 self._handler_request_noblock(), 此时这里的 self 是 WSGIServer 对象。 WSGIServer 通过 selector 接收到请求之后,会调用 self._handle_request_noblock() 方法,
def serve_forever(self, poll_interval=0.5):
self.__is_shut_down.clear()
try:
with _ServerSelector() as selector:
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request:
ready = selector.select(poll_interval)
if self.__shutdown_request:
break
if ready:
self._handle_request_noblock()
self.service_actions()
finally:
...
1. _handle_request_noblock
因为这个 _handler_request_noblock() 方法,只在 socketserver.BaseServer 中定义了,那么就直接使用 BaseServer._handler_request_noblock() 方法了,这方法定义如下:
def _handle_request_noblock(self):
try:
request, client_address = self.get_request()
except OSError:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except Exception:
self.handle_error(request, client_address)
self.shutdown_request(request)
except:
self.shutdown_request(request)
raise
else:
self.shutdown_request(request)
2. self.get_request && self.socket.accept
get_request 方法是在 TCPServer 类中实现的,这个方法中只有一条指令: return self.socket.accept(),我们直接看self.socket.accept() 方法。在 TCPServer 类实例化的时候, 实例化一个 socket.socket 的对象,accept 方法,也就是这个对象的方法,这个方法的主要作用是接受这个请求。
注意:
这里的 request 只是一个 socket 对象, 并不是我们写view方法中的request,不要误认为 request 对象是在这里生成的
3. self.process_request
重点来了, 这个方法是整条流中的核心,他的作用是接收数据流,生成request对象,对请求做处理,返回等,这个方法主要是启动了一个线程来执行 process_request_thread 方法,然后 process_request_thread 方法又调用了 finish_request 方法,所以我们直接来看 finish_request 方法, finish_request 是在 BaseServer 中实现的,方法中只有一条指令,那么这个 RequestHandlerClass 又是谁呢?
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
我们看一下 BaseServer 在实例化的时候,会接受一个 RequestHandlerClass 参数,那我们什么时候传给他的这个参数呢? 那就是我们在启动 WSGIServer 的时候传给他的。还记得启动 WSGIServer 的时候创建的那个 httpd 对象吗? 我们再来看一下 WSGIServer 启动时的代码:
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading:
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
在创建 httpd_cls 类的指令是这样的 httpd_cls = type(‘WSGIServer’, (socketserver.ThreadingMixIn, server_cls), {}) ,这个 httpd_cls 继承了 socketserver.ThreadingMixIn, server_cls 两个类, 所以我们实例化 httpd_cls 类的时候,会调用父类的 __init__ 方法,所以这时候这个 RequestHandlerClass 就等于 WSGIRequestHandler,到了这里我们再去看 finish_request 方法,就很容易理解了, finish_request 方法中就是实例化一个 WSGIRequestHandler 的对象。 那我们在看一下实例化这个对象的时候,会做什么? 那我们就看一下 WSGIRequestHandler 这个类的实现。
4. WSGIRequestHandler类的实现
首先我们看一下 WSGIRequestHandler 的继承关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYWgLSog-1598856705341)(./1598599503538.png)]
同 WSGIServer 同理,进行实例化的时候,会调用 socketserver.BaseRequestHandler 的 __init__ 方法, 那我们就直接看socketserver.BaseRequestHandler 的 __init__ 方法的源码:
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
前面的赋值操作,我们忽略前面的赋值操作,直接看 self.setup() 方法, 首先我们要知道这里的 self 是 WSGIRequestHandler 类,因为我们正在 finish_request 中实例化 WSGIRequestHandler类。所以这里的 self.setup方法,我们要从 WSGIRequestHandler 类中向上找,所以这里会执行 StreamRequestHandler.setup 方法
def setup(self):
self.connection = self.request
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
if self.wbufsize == 0:
self.wfile = _SocketWriter(self.connection)
else:
self.wfile = self.connection.makefile('wb', self.wbufsize)
这里的 setup 方法就是创建与 socket 关联的文件对象,执行完setup后,在去执行handle 方法,在 handle 方法中会调用 handle_one_request 方法,这个方法首先会从 socket 中读取数据,然后调用 parse_request 方法对数据做解析,parse_request 是对 HTTP 版本和 header长度 做校验。 然后创建一个 ServerHandler 的对象,这个对象在实例化的时候,会读取socket中的数据保存到对象的属性中。当我们在实例化 ServerHandler 的时候,调用了 self.get_environ 方法,这个方法是对请求头部的一个整理到一个 env 的变量中,具体的内容可以去看一下 get_environ 方法的代码,这里不做解释。 最后调用 handler.run 方法,调用 handler.run 的时候,调用了 server.get_app 方法,这个server又是哪里来的呢,还是要看上面 finish_request 方法在实例化 WSGIRequestHandler 的时候,传入了一个 self 参数,这个 **self ** 是 WSGIServer, 这里获取到的 application 是一个 StaticFilesHandler对象, 至于为什么请看上一篇文章:启动WSGIServer
def handle_one_request(self):
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(414)
return
if not self.parse_request(): # An error code has been sent, just exit
return
handler = ServerHandler(
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
)
handler.request_handler = self # backpointer for logging & connection closing
handler.run(self.server.get_app())
我看继续看一下 handler.run 方法,这个方法中,调用了 application,因为 application 是一个 StaticFilesHandler 对象,直接调用的话,那就会调用 StaticFilesHandler 类的 __call__ 方法。
def run(self, application):
try:
self.setup_environ()
self.result = application(self.environ, self.start_response)
self.finish_response()
except:
try:
self.handle_error()
except:
# If we get an error handling an error, just give up already!
self.close()
raise # ...and let the actual server figure it out.
在 StaticFilesHandler 中,还会调用 StaticFilesHandler 自己的 application属性,具体代码如下,这里的 self 就是 StaticFilesHandler对象自身了, 由上一篇文章可知,这里的 StaticFilesHandler 的 application 是一个 WSGIHandler 对象, 所以这里调用的时候,会直接调用 WSGIHandler 的 __call__ 方法。
if not self._should_handle(get_path_info(environ)):
return self.application(environ, start_response)
return super().__call__(environ, start_response)
那我们直接看 WSGIHandler 的 __call__ 方法,首先 request = self.request_class(environ)
这条指令就是生成 request 对象的指令,我们在 view 中写的代码,用的就是这个 request对象,这个对象实例化的是什么内容,可以自己去看一下,比较简单。在看一下 response = self.get_response(request)
指令,这条指令调用了 get_response 的方法
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
response_headers = [
*response.items(),
*(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
]
start_response(status, response_headers)
if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
response = environ['wsgi.file_wrapper'](response.file_to_stream)
return response
5. get_response方法
那我们在看一下 get_response 方法的内容,这里主要是调用了 _middleware_chain
def get_response(self, request):
"""Return an HttpResponse object for the given HttpRequest."""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
response._closable_objects.append(request)
if response.status_code >= 400:
log_response(
'%s: %s', response.reason_phrase, request.path,
response=response,
request=request,
)
return response
在上一篇文章中 我们详细解释了这个 _middleware 的结构,不清楚的同学可以去看一下。
在调用 _middleware 这个方法的时候,首先会调用最外层对象的 __call__ 方法,我们可以看一下中间件的写法,都会继承父类 MiddlewareMixin,当然也会继承 MiddlewareMixin.__call__ 方法,下面是 MiddlewareMixin.__call__ 方法的代码,首先会判断这个对象中有没有 process_request 方法,如果有那就调用,并将返回值传给 response 变量,然后调用自己的 get_response 方法,此时,如果不是在最内曾,那么自己的 get_response 指向的就是内层的中间件对象,然后在执行内层的 process_request 方法,如果是最内层,那么自己的 get_response 方法就是 _get_response 方法了。
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
那我们看一下 _get_response 方法,这个方法比较长, 我们就慢慢来看,首先会通过 get_resolver 方法创建一个 URLResolver 的对象,用来通过 url 找到你写的 view 方法或者类
,callback 这个变量就是获取到的方法或者类。然后执行 _view_middleware 列表中的方法。这个列表是在 load_middleware 中处理的,里面存放的是中间件的 process_view 方法,这也就是将每个中间件的 process_view 方法执行一遍。 然后执行自己写的 view 中的类或者方法,然后判断 response 是否为空,是否有 render,之后将 response 返回。
def _get_response(self, request):
response = None
if hasattr(request, 'urlconf'):
urlconf = request.urlconf
set_urlconf(urlconf)
resolver = get_resolver(urlconf)
else:
resolver = get_resolver()
resolver_match = resolver.resolve(request.path_info)
callback, callback_args, callback_kwargs = resolver_match
request.resolver_match = resolver_match
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs)
except Exception as e:
response = self.process_exception_by_middleware(e, request)
if response is None:
if isinstance(callback, types.FunctionType): # FBV
view_name = callback.__name__
else: # CBV
view_name = callback.__class__.__name__ + '.__call__'
raise ValueError(
"The view %s.%s didn't return an HttpResponse object. It "
"returned None instead." % (callback.__module__, view_name)
)
elif hasattr(response, 'render') and callable(response.render):
for middleware_method in self._template_response_middleware:
response = middleware_method(request, response)
if response is None:
raise ValueError(
"%s.process_template_response didn't return an "
"HttpResponse object. It returned None instead."
% (middleware_method.__self__.__class__.__name__)
)
try:
response = response.render()
except Exception as e:
response = self.process_exception_by_middleware(e, request)
return response
在这里执行完 _get_response 之后,我们需要再回到中间件,因为我们是从中间件 __call__ 方法跳过来的, 下一步我们需要判断中间件有没有 process_response 了,如果有的话,我们需要执行一下。
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
之后,我们处理一下response,关闭连接,这样一次请求就完成了。