中间件
使用:
- 编写类,在类型定义:process_request、process_view、process_response
- 中间件注册,在settings中的配置。
编写类
from django.utils.deprecation import MiddlewareMixin
class TempMiddleware(MiddlewareMixin):
def process_request(self, request):
# request是请求相关所有的数据
pass
def process_view(self, request, view, *args, **kwargs):
# request是请求相关所有的数据; view是试图函数; 路由参数*args, **kwargs
pass
def process_response(self, request, response):
# request是请求相关所有的数据
# response是试图函数返回的那个对象(封装了要返回到用户浏览器的所有数据)
return response
注册
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"utils.md.KeLaMiddleware"
]
# 动态导入 + 反射
views.py
from django.contrib import admin
from django.urls import path
from django.shortcuts import HttpResponse
def x1(request):
print("视图.x1")
return HttpResponse("x1")
def x2(request, v1):
print("视图.x2", v1)
return HttpResponse("x2")
urlpatterns = [
path('admin/', admin.site.urls),
path('x1/', x1),
path('x2/<int:v1>/', x2),
]
效果:
作用
基于中间件可以做什么?
- 根据请求周期,对 request 进行赋值,后续方便进行调用。
- 根据请求周期,对业务逻辑代码进行自定义,决定是否可以继续向后
- return None,继续向后走
- return HttpResponse对象
根据请求周期,对返回给用户浏览器的数据进行自定义:删除内容、增加、cookie、响应头...
注意
这个中间件和nginx apache这样的中间件不一样。比如做前置代理,做https
Django中间件 / java的拦截器 / c#的RequestHanler
比如我们做一个token限制,需要传参token才能访问:
#md.py
def process_request(self, request):
# request是请求相关所有的数据
if request.path_info == "/xxx/":
token = request.GET.get("token")
if token == "qwe123":
return
return HttpResponse("permission error!!!")
#views.py
def xxx(request):
return HttpResponse("succeed")
urlpatterns = [
path('admin/', admin.site.urls),
path('x1/', x1),
path('x2/<int:v1>/', x2),
path('xxx/', xxx),
]
中间件源码
从uwsgi点到basehandler:
load_middleware:
异步的部分删除了
def load_middleware(self, is_async=False):
"""
Populate middleware lists from settings.MIDDLEWARE.
Must be called after the environment is fixed (see __call__ in subclasses).
"""
#列表存放中间件的方法
self._view_middleware = []
self._template_response_middleware = []
self._exception_middleware = []
#闭包,函数->内部调用
handler = convert_exception_to_response(get_response)
[inner(), self._get_response]
#翻转获取的中间件
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
# Adapt handler, if needed.
adapted_handler = self.adapt_method_mode(
middleware_is_async, handler, handler_is_async,
debug=settings.DEBUG, name='middleware %s' % middleware_path,
)
mw_instance = middleware(adapted_handler)
#自动执行类中的call方法
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if str(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue
else:
handler = adapted_handler
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,
self.adapt_method_mode(is_async, mw_instance.process_view),
)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(
self.adapt_method_mode(is_async, mw_instance.process_template_response),
)
if hasattr(mw_instance, 'process_exception'):
# The exception-handling stack is still always synchronous for
# now, so adapt that way.
self._exception_middleware.append(
self.adapt_method_mode(False, mw_instance.process_exception),
)
handler = convert_exception_to_response(mw_instance)
中间件的请求到来:
class WSGIHandler(base.BaseHandler):
#请求的封装
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'):
# If `wsgi.file_wrapper` is used the WSGI server does not call
# .close on the response, but on the file wrapper. Patch it to use
# response.close instead which takes care of closing all files.
response.file_to_stream.close = response.close
response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size)
return response
#get_response
def get_response(self, request):
#触发 self._middleware_chain=handler
#逐步执行中间件的process_request注册顺序,从前到后
#执行self._get_response[路由匹配]
#中间件的process_view,视图函数,中间件的process_exception,
#中间件的process_template_response
#中间件中的process_response按照中间件的注册顺序,从后到前
response = self._middleware_chain(request)
response._resource_closers.append(request.close)
if response.status_code >= 400:
log_response(
'%s: %s', response.reason_phrase, request.path,
response=response,
request=request,
)
return response
#_get_response
def _get_response(self, request):
response = None
#路由匹配
#callback=视图函数 callback_args, callback_kwargs 动态路由传递的参数
#request.resolve_match()
callback, callback_args, callback_kwargs = self.resolve_request(request)
#执行中间件所有的process_view
for middleware_method in self._view_middleware:
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
if response is None:
#django内部数据库支持的事务
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:
raise
# 给出报错
self.check_response(response, callback)
#执行process_template_response
if hasattr(response, 'render') and callable(response.render):
for middleware_method in self._template_response_middleware:
response = middleware_method(request, response)
try:
response = response.render()
except Exception as e:
response = self.process_exception_by_middleware(e, request)
if response is None:
raise
return response