本文环境python3.5.2,django1.10.x系列
python manage.py runserver 命令启动本地调试服务器后,接着要分析一下django对请求request的整个处理过程,在此感谢在django源码学习中 小屋子大侠 的帮助,
小屋子大侠 博文地址:https://blog.csdn.net/qq_33339479/article/category/6800733
一、创建WSGIServer
上一节讲到在使用runserver运行Django项目,在启动时会调用django.core.servers.basehttp中的run()方法,创建一个django.core.servers.basehttp.WSGIServer类的实例,之后调用其serve_forever()方法启动HTTP服务。run方法的源码如下:
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
server_address = (addr, port) # 服务端监听的地址和端口
if threading: # 如果多线程运行
httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {}) # 创建WSGIServer服务器,生成一个
else:
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6) # 实例化该类
# Sets the callable application as the WSGI application that will receive requests
httpd.set_app(wsgi_handler) # 设置客服务端的处理handler, 即application
httpd.serve_forever()
如上,我们可以看到:在创建WSGIServer实例的时候会指定HTTP请求的Handler,上述代码使用WSGIRequestHandler。当用户的HTTP请求到达服务器时,WSGIServer会创建WSGIRequestHandler实例,使用其handler方法来处理HTTP请求(其实最终是调用wsgiref.handlers.BaseHandler中的run方法处理)。WSGIServer通过set_app方法设置一个可调用(callable)的对象作为application,上面提到的handler方法最终会调用设置的application处理request,并返回response。
其中,WSGIServer继承自wsgiref.simple_server.WSGIServer,而WSGIRequestHandler继承自wsgiref.simple_server.WSGIRequestHandler,wsgiref是Python标准库给出的WSGI的参考实现。
class WSGIServer(HTTPServer): # WSGIServer继承自HTTPServer,HTTPServer继承自TCPServer
"""BaseHTTPServer that implements the Python WSGI protocol"""
application = None
def server_bind(self):
"""Override server_bind to store the server name."""
HTTPServer.server_bind(self)
self.setup_environ()
def setup_environ(self): # 设置env环境参数
# Set up base environment
env = self.base_environ = {}
env['SERVER_NAME'] = self.server_name
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['SERVER_PORT'] = str(self.server_port)
env['REMOTE_HOST']=''
env['CONTENT_LENGTH']=''
env['SCRIPT_NAME'] = ''
def get_app(self): # 获得处理器handler
return self.application
def set_app(self,application): # 设置处理器handler
self.application = application
二、处理Request
第一步中说到的application,即为django.core.management.commands.runserver中的get_handler()方法
def get_internal_wsgi_application():
"""
Loads and returns the WSGI application as configured by the user in
``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
this will be the ``application`` object in ``projectname/wsgi.py``.
This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
for Django's internal server (runserver); external WSGI servers should just
be configured to point to the correct application object directly.
If settings.WSGI_APPLICATION is not set (is ``None``), we just return
whatever ``django.core.wsgi.get_wsgi_application`` returns.
"""
from django.conf import settings # 加载生成project时生成的setting文件
app_path = getattr(settings, 'WSGI_APPLICATION') # 检查settings配置文件中是否设置“WSGI_APPLICATION”
if app_path is None: # 如果没有设置,就使用默认的application
return get_wsgi_application()
try:
return import_string(app_path) # 如果有设置,就返回设置的application
except ImportError as e:
msg = (
"WSGI application '%(app_path)s' could not be loaded; "
"Error importing module: '%(exception)s'" % ({
'app_path': app_path,
'exception': e,
})
)
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
sys.exc_info()[2])
当settings文件中未设置WSGI_APPLICATION,就会使用django默认的内部wsgi方法,其中,get_wsgi_application()方法就是调用此方法,该函数在django.core.wsgi.py文件中
def get_wsgi_application():
"""
The public interface to Django's WSGI support. Should return a WSGI
callable.
Allows us to avoid making django.core.handlers.WSGIHandler public API, in
case the internal WSGI implementation changes or moves in the future.
"""
django.setup(set_prefix=False) # 加载django初始环境
return WSGIHandler() # 返回WSGI处理器
此方法在加载django处理化环境后,返回一个WSGIHandler类,下面我们看看这个处理器中的代码
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest # 调用WSGIResquest对象
def __init__(self, *args, **kwargs):
super(WSGIHandler, self).__init__(*args, **kwargs)
self.load_middleware() # 加载所有的中间件
def __call__(self, environ, start_response): # 类实例化调用此方法
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ) # 信号发送,向接受通知的注册者发送通知
try:
request = self.request_class(environ) # 实例化WSGIRequest类,获得request
except UnicodeDecodeError:
logger.warning(
'Bad Request (UnicodeDecodeError)',
exc_info=sys.exc_info(),
extra={
'status_code': 400,
}
)
response = http.HttpResponseBadRequest()
else:
response = self.get_response(request) # get_response方法处理request
response._handler_class = self.__class__ # response的_handler_class为当前处理类
status = '%d %s' % (response.status_code, response.reason_phrase) # 获取状态码和相应描述
response_headers = [(str(k), str(v)) for k, v in response.items()] # 获取返回头信息
for c in response.cookies.values(): # 获取cookies
response_headers.append((str('Set-Cookie'), str(c.output(header='')))) # 将cookies加到返回头重
start_response(force_str(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) # 通过wsgi的文件传输方法处理响应的文件传输
return response # 返回response
该类继承自base.BaseHandler类,其中load_middleware()和get_response(request) 方法均使用BaseHandler类中的方法
下面分析一下BaseHandler类中的代码
class BaseHandler(object):
def __init__(self):
self._request_middleware = None
self._view_middleware = None
self._template_response_middleware = None
self._response_middleware = None
self._exception_middleware = None
self._middleware_chain = None
def load_middleware(self):
"""
Populate middleware lists from settings.MIDDLEWARE (or the deprecated
MIDDLEWARE_CLASSES).
Must be called after the environment is fixed (see __call__ in subclasses).
"""
self._request_middleware = [] # 列表承载各个middleware中相应的方法
self._view_middleware = []
self._template_response_middleware = []
self._response_middleware = []
self._exception_middleware = []
if settings.MIDDLEWARE is None: # 旧版使用settings.MIDDLEWARE_CLASSES,新版本全部使用settings.MMIDDLEWARE
warnings.warn( # 此处主要是为了兼容旧版
"Old-style middleware using settings.MIDDLEWARE_CLASSES is "
"deprecated. Update your middleware and use settings.MIDDLEWARE "
"instead.", RemovedInDjango20Warning
)
handler = convert_exception_to_response(self._legacy_get_response)
for middleware_path in settings.MIDDLEWARE_CLASSES:
mw_class = import_string(middleware_path)
try:
mw_instance = mw_class()
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if hasattr(mw_instance, 'process_request'):
self._request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)
else: # 新版处理方法
handler = convert_exception_to_response(self._get_response) # 将响应加异常处理,self._get_response方法为处理请求返回响应的最重要方法
for middleware_path in reversed(settings.MIDDLEWARE): # 获取中间件路径
middleware = import_string(middleware_path) # 获取中间件类
try:
mw_instance = middleware(handler) # 初始化中间件类
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
if mw_instance is None:
raise ImproperlyConfigured(
'Middleware factory %s returned None.' % middleware_path
)
if hasattr(mw_instance, 'process_view'): # 判断中间件中是否定义各方法,有则取出放到各个列表中
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)
handler = convert_exception_to_response(mw_instance) # 同样,将初始化后的类包装异常处理
# We only assign to this when initialization is complete as it is used
# as a flag for initialization being complete.
self._middleware_chain = handler # 将最后一个中间件实例传给该属性
def make_view_atomic(self, view):
non_atomic_requests = getattr(view, '_non_atomic_requests', set()) # 获取view是否有非事务属性
for db in connections.all(): # 遍历设置的数据库
if db.settings_dict['ATOMIC_REQUESTS'] and db.alias not in non_atomic_requests: # 如果数据库中有配置ATOMIC_REQUESTS属性为TRUE,每个请求一个事务并且该数据库不在非事务列表里
view = transaction.atomic(using=db.alias)(view) # 处理view就用一个事务处理
return view
def get_exception_response(self, request, resolver, status_code, exception):
return get_exception_response(request, resolver, status_code, exception, self.__class__) # 返回异常响应
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) # 执行该handler的__call__方法,返回响应值
# This block is only needed for legacy MIDDLEWARE_CLASSES; if
# MIDDLEWARE is used, self._response_middleware will be empty.
try:
# Apply response middleware, regardless of the response
for middleware_method in self._response_middleware: # 遍历中间件中的process_response方法,处理响应
response = middleware_method(request, response)
# Complain if the response middleware returned None (a common error).
if response is None:
raise ValueError(
"%s.process_response didn't return an "
"HttpResponse object. It returned None instead."
% (middleware_method.__self__.__class__.__name__))
except Exception: # Any exception should be gathered and handled
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())
response._closable_objects.append(request) #
# If the exception handler returns a TemplateResponse that has not
# been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)): # 查看响应中是否需要渲染,并可该方法可调用
response = response.render() # 返回响应
if response.status_code == 404:
logger.warning(
'Not Found: %s', request.path,
extra={'status_code': 404, 'request': request},
)
return response # 返回响应
def _get_response(self, request):
"""
Resolve and call the view, then apply view, exception, and
template_response middleware. This method is everything that happens
inside the request/response middleware.
"""
response = None
if hasattr(request, 'urlconf'):
urlconf = request.urlconf # 获取请求的urlconf,是否配置urlconf,如果没有,则默认使用django默认
set_urlconf(urlconf)
resolver = get_resolver(urlconf) # 实际为初始化django.urls.resolver.py文件下的RegexURLResolver()类
else:
resolver = get_resolver()
resolver_match = resolver.resolve(request.path_info) # 调用py文件下的RegexURLResolver.resolve()函数,根据路径获得处理函数
callback, callback_args, callback_kwargs = resolver_match # 返回处理函数,参数
request.resolver_match = resolver_match
# Apply view middleware
for middleware_method in self._view_middleware: # 处理中间件中的‘process_view’函数
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) # 处理函数处理request请求,返回响应
except Exception as e:
response = self.process_exception_by_middleware(e, request)
# Complain if the view returned None (a common error).
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)
)
# If the response supports deferred rendering, apply template
# response middleware and then render the response
elif hasattr(response, 'render') and callable(response.render): # 判断响应中是否需要渲染并且可调用
for middleware_method in self._template_response_middleware: # 如果有,则调用中间件中模版函数处理
response = middleware_method(request, response)
# Complain if the template response middleware returned None (a common error).
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 # 返回响应值
def process_exception_by_middleware(self, exception, request):
"""
Pass the exception to the exception middleware. If no middleware
return a response for this exception, raise it.
"""
for middleware_method in self._exception_middleware:
response = middleware_method(request, exception)
if response:
return response
raise
def handle_uncaught_exception(self, request, resolver, exc_info):
"""Allow subclasses to override uncaught exception handling."""
return handle_uncaught_exception(request, resolver, exc_info)
def _legacy_get_response(self, request): # 旧版本中间件中使用的方法
"""
Apply process_request() middleware and call the main _get_response(),
if needed. Used only for legacy MIDDLEWARE_CLASSES.
"""
response = None
# Apply request middleware
for middleware_method in self._request_middleware: # 遍历处理中间件中‘process_request’方法
response = middleware_method(request)
if response:
break
if response is None:
response = self._get_response(request) # 同样调用_get_response()方法
return response
其中_get_response方法为处理请求,获得响应的最主要方法,下面讲一下此方法中的get_resolver()方法,其中主要是调用实际为初始化django.urls.resolver.py文件下的RegexURLResolver()类中的resolve()方法,下面分析一下resolve()方法
def resolve(self, path):
path = force_text(path) # path may be a reverse_lazy object # 将路径强制转换成字符串
tried = [] # 用于记录已解析对象(路径)
match = self.regex.search(path) # 通过继承对象LocaleRegexProvider.regex方法来匹配路径,由于整个当前对象RegexURLResolver初始化时,传递的参数是'^/'
if match:
new_path = path[match.end(): # 如果匹配到,则取后面匹配到的剩余的path地址
for pattern in self.url_patterns: # 遍历各个url_pattern
try:
sub_match = pattern.resolve(new_path) # pattern就是<RegexURLResolver <RegexURLPattern list> >这个对象,pattern.resolve相当于RegexURLResolver.resolve
except Resolver404 as e: # 如果匹配成功,就会返回django.urls.resolver.ResolverMatch的类
sub_tried = e.args[0].get('tried')
if sub_tried is not None:
tried.extend([pattern] + t for t in sub_tried)
else:
tried.append([pattern])
else:
if sub_match:
# Merge captured arguments in match with submatch
sub_match_dict = dict(match.groupdict(), **self.default_kwargs)
sub_match_dict.update(sub_match.kwargs) # 添加前面匹配的kwargs值
# If there are *any* named groups, ignore all non-named groups.
# Otherwise, pass all non-named arguments as positional arguments.
sub_match_args = sub_match.args
if not sub_match_dict:
sub_match_args = match.groups() + sub_match.args
return ResolverMatch( # 返回ResolverMath初始化
sub_match.func,
sub_match_args,
sub_match_dict,
sub_match.url_name,
[self.app_name] + sub_match.app_names,
[self.namespace] + sub_match.namespaces,
)
tried.append([pattern])
raise Resolver404({'tried': tried, 'path': new_path})
raise Resolver404({'path': path})
这里简化的讲了url是如何匹配获得处理函数以及url中的参数, 此时我们继续分析url中配置的view视图函数,举一个resrful风格的例子
from django.conf.urls import url # 这里是重点
urlpatterns = [
url(r'^detial/', view.DetailView.as_view(), name='detail'),
]
class DetailView(Apiview):
return Response({"detail":"请求成功"})
下面主要就url函数功能,和as_view()函数进行分析
def url(regex, view, kwargs=None, name=None): # url函数,其中regex是r'^detial/', view就是view.DetailView.as_view(), name是‘detail’
if isinstance(view, (list, tuple)): # 判断view是否是列表或者元组,这是显然不是
# For include(...) processing.
urlconf_module, app_name, namespace = view
return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
elif callable(view): # 判断view函数是否是可调用的
return RegexURLPattern(regex, view, kwargs, name) # 如果是,则初始化RegexURLPattern实例
else:
raise TypeError('view must be a callable or a list/tuple in the case of include().')
url函数主要url匹配,处理函数和name传给RegexURLPattern类,并初始化,然后通过该类进行请求url的匹配工作
下面分析一下as_view()方法,所有的处理函数继承自view函数,调用as_view()方法,然后调用内包函数view(),最后调用处理请求的dispath函数处理请求,获得返回值
class View(object):
"""
Intentionally simple parent class for all views. Only implements
dispatch-by-method and simple sanity checking.
"""
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 定义请求方法
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
# Go through keyword arguments, and either save their values to our
# instance, or raise an error.
for key, value in six.iteritems(kwargs): # 初始化,将输入参数变为类属性
setattr(self, key, value)
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
if key in cls.http_method_names: # 判断输入参数是否与请求方法重名
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key): # 判断输入参数是否已经存在
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'): # 判断是否有‘get’没有‘head’,则head请求等同于get请求
self.head = self.get
self.request = request # 将输入的参数转变为属性
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs) # 调用处理函数
view.view_class = cls # 为view函数赋值
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=()) # 更新view中的参数值,将cls的属性赋给view
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=()) # 同上
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names: # 取请求方法的小写,
handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 判断view类中是否存在此方法,如果存在,则取出此方法
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 返回handler处理request的响应值
def http_method_not_allowed(self, request, *args, **kwargs): # 请求方法不被允许,处理函数
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return http.HttpResponseNotAllowed(self._allowed_methods())
def options(self, request, *args, **kwargs):
"""
Handles responding to requests for the OPTIONS HTTP verb.
"""
response = http.HttpResponse()
response['Allow'] = ', '.join(self._allowed_methods())
response['Content-Length'] = '0'
return response
def _allowed_methods(self): # 返回当前类允许的类方法
return [m.upper() for m in self.http_method_names if hasattr(self, m)]
本章节主要讲解url请求时,如果进行匹配,并且如何通过wsgi处理请求并返回响应值