Django中间件机制
Django中的每一个请求都要通过中间件,先执行所有中间件的process_request函数,返回再执行所有的process_response。
# django中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.local.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'
]
# 编写自己的中间件
class CommonMiddleware(object):
def process_request(self, request):
return None
def process_response(self, request, response):
return response
# process_view 等等
# 部分源码
class BaseHandler(object):
def load_middleware(self):
if settings.MIDDLEWARE is None:
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: # 获取设置中的中间件, for循环是有顺序的
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) # 先处理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)
Django/social-auth管道机制
管道机制在linux是指由一个进程的连接数据流向另一个进程,属于进程通信的一种。 这里的django管道机制,是指由一个函数的数据流向一个函数,类管道机制。 本文以python-social-auth为例。在social-auth中,我们可以在管道中随意添加函数, 以满足我们的需求,其特点是前一个函数的返回值(定义为字典数据类型), 作为后一个函数的参数,直到执行到最后,管道执行完成。
SOCIAL_AUTH_PIPELINE: ( # socia_auth 默认管道设置
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.tests.pipeline.ask_for_password',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.tests.pipeline.set_password',
'social_core.pipeline.user.user_details'
)
这里以微信授权登录(token)为例,理解一下登录过程。
- 首先post请求thirdparty-auth/social/token_user/ 参数provider,code, 授权登录
- 通过code换取用户信息,通过auth_comlete()函数保存access_token和用户信息
通过authenticate()函数开始管道之旅
# social_core/backends/base.py def authenticate(self, *args, **kwargs): """Authenticate user using social credentials Authentication is made if this is the correct backend, backend verification is made by kwargs inspection for current backend name presence. """ # Validate backend and arguments. Require that the Social Auth # response be passed in as a keyword argument, to make sure we # don't match the username/password calling conventions of # authenticate. if 'backend' not in kwargs or kwargs['backend'].name != self.name or \ 'strategy' not in kwargs or 'response' not in kwargs: return None self.strategy = kwargs.get('strategy') or self.strategy self.redirect_uri = kwargs.get('redirect_uri') or self.redirect_uri self.data = self.strategy.request_data() kwargs.setdefault('is_new', False) # 默认设is_new为False pipeline = self.strategy.get_pipeline(self) # 得到上面设置的管道 args, kwargs = self.strategy.clean_authenticate_args(*args, **kwargs) return self.pipeline(pipeline, *args, **kwargs) # 开始管道 def pipeline(self, pipeline, pipeline_index=0, *args, **kwargs): out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) # 执行管道里的函数 if not isinstance(out, dict): return out user = out.get('user') if user: user.social_user = out.get('social') # 额外的两个参数 user.is_new = out.get('is_new') return user # 返回user def run_pipeline(self, pipeline, pipeline_index=0, *args, **kwargs): # 核心 out = kwargs.copy() out.setdefault('strategy', self.strategy) out.setdefault('backend', out.pop(self.name, None) or self) out.setdefault('request', self.strategy.request_data()) out.setdefault('details', {}) if not isinstance(pipeline_index, int) or \ pipeline_index < 0 or \ pipeline_index >= len(pipeline): pipeline_index = 0 for idx, name in enumerate(pipeline[pipeline_index:]): # 管道机制原来是一个for循环 out['pipeline_index'] = pipeline_index + idx func = module_member(name) # 取出函数 result = func(*args, **out) or {} # 执行函数 if not isinstance(result, dict): # 不为字典返回 return result out.update(result) # 这是最关键的一步,它把返回更新到out,供下一次循环里函数的参数 return out def module_member(name): mod, member = name.rsplit('.', 1) # 以.分割,返回两个 module = import_module(mod) # 导入模块 return getattr(module, member) # 返回函数 # 我们随便找两个函数看看 def social_details(backend, details, response, *args, **kwargs): return {'details': dict(backend.get_user_details(response), **details)} def social_uid(backend, details, response, *args, **kwargs): return {'uid': backend.get_user_id(details, response)} # result = func(*args, **out) 先执行social_details, 然后返回字典里包含details, 这个detail就作为第二次循环执行social_uid里参数的details
参考
https://docs.djangoproject.com/en/2.1/topics/http/middleware/
https://python-social-auth-docs.readthedocs.io/en/latest/