Django中间件和管道机制

Django中间件机制

Django中的每一个请求都要通过中间件,先执行所有中间件的process_request函数,返回再执行所有的process_response。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 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中,我们可以在管道中随意添加函数,
以满足我们的需求,其特点是前一个函数的返回值(定义为字典数据类型), 作为后一个函数的参数,直到执行到最后,管道执行完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
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()函数开始管道之旅
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    # 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/

----------本文完,感谢您的阅读----------