Django-session中间件源码简单分析

settings里有关中间件的配置

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',
'rbac.service.middleware.M1'
] from django.contrib.sessions.middleware import SessionMiddleware

可以看到settings中都是字符串形式的,我们通过from django.contrib.sessions.middleware import SessionMiddleware导入,并查看SessionMiddleware类的内容

SessionMiddleware

import time
from importlib import import_module from django.conf import settings
from django.contrib.sessions.backends.base import UpdateError
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import cookie_date class SessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key) def process_response(self, request, response):
"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# Save the session data and refresh the client cookie.
# Skip session save for 500 responses, refs #3881.
if response.status_code != 500:
try:
request.session.save()
except UpdateError:
raise SuspiciousOperation(
"The request's session was deleted before the "
"request completed. The user may have logged "
"out in a concurrent request, for example."
)
response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
)
return response

初始化方法

我们先看这个类的init初始化方法

def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore

首先定义self.get_response=get_response这里默认为None

然后通过import_module导入模块,该方法可以通过字符串导入模块

我们到settings中找到SESSION_ENGINE参数的值

这里engine就等于上面导入的模块

然后self.SessionStore = engine.SessionStore,我们可以看看engine模块中都有什么

可以看到SessionStore就是一个类

self.SessionStore就是这个类

process_request

def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)

首先定义一个session_key

这里的session_key其实就是COOKIE里带的以sessionid为key,随机字符串为值的值

当我们第一次访问时其实session_key的值应该是None

然后定义request.session,我们可以看到我们一直使用的request.session其实是self.SessionStore这个类实例化出的对象

实例化的过程执行了什么呢

SessionStore类初始化时调用了其父类的__init__方法

父类的__init__方法首先定义了self._session_key=session_key,第一次访问和默认情况都是None

然后定义了两个变量self.accessed和self.modified都是False

最后的self.serializer是序列化时的方法,我们暂时不看

到此process_request就执行完了

视图函数

在执行视图函数时我们可能会对request.session进行操作,例如登录成功后我们要做以下设置

request.session["user_id"] = user.pk

我们在学习面向对象的知识时曾经学过这种类似于操作字典一样的方法,在对象的父类中需要有__getitem__,__setitem__,__delitem__方法,才能这么操作,而这么操作了也就相当于执行了前面的几个方法

在SessionStore的父类SessionBase中我们找到了这几个方法

我们这里的操作其实就是执行的下面的代码

def __setitem__(self, key, value):
self._session[key] = value
self.modified = True

首先self._session[key] = value,这个self._session是个什么呢

继续在SessionBase类中查找

从上面的代码可以看出self._session其实就是self._session_cache,如果第一次访问,self._session_cache不存在,所以会抛出异常,走except的内容,给self._session_cache赋一个空字典,这里给这个字典添加了我们设置的键值对

然后将self.accessed和self.modified的值变为True

当视图函数执行完成后,我们又要走中间件的process_response方法

process_response

def process_response(self, request, response):
"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# Save the session data and refresh the client cookie.
# Skip session save for 500 responses, refs #3881.
if response.status_code != 500:
try:
request.session.save()
except UpdateError:
raise SuspiciousOperation(
"The request's session was deleted before the "
"request completed. The user may have logged "
"out in a concurrent request, for example."
)
response.set_cookie(
settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
)
return response

首先定义了三个变量

如果执行了上面视图函数中的操作,那么accessed和modified应该都是True

is_empty()执行的代码如下

从前面的代码可以看到我们的self._session其实就是_session_cache,而我们给他做了赋值,所有这里的empty是False

接着往下走

前面的赋值操作未出现异常我们执行else后的if判断,由于empty为False,所以执行else后的代码

这里可以看到其实就是定义了session的过期时间等相关的属性

接着,当后台运行没错,状态码不是500时,我们执行request.session.save(),这里save具体做了什么呢

首先判断self.session_key是否为None,如果是第一次访问则为None,执行self.create()

这个方法里首先用self._get_new_session_key()方法生成了一个随机字符串赋值给self._session_key,然后继续执行self.save方法,并且设置must_create=True

这时再进入save方法,设置data = self._get_session(no_load=must_create),self._get_session方法执的结果其实就是上面的self._session_cache,就是我们设置的有键值对的字典

所以data的值就是我们设置的保存用户信息的字典

然后执行obj = self.create_model_instance(data),self.create_model_instance(data)执行下面的代码

其实就是返回了一个包含session_key,session_data等字段的model对象

然后调用事务,将数据保存到数据库

这里第一次访问时,must_create是True,所以是添加操作,而当后面再访问时,must_create是False,所以执行的是更新操作

数据保存完成后,我们再用response.set_cookie设置了COOKIE值,最后将结果返回

Django-session中间件源码简单分析的更多相关文章

  1. negroni-gzip源码简单分析解读

    negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...

  2. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  3. FFmpeg源码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  4. FFmpeg源码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  5. FFmpeg的HEVC解码器源码简单分析:概述

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  6. FFmpeg的HEVC解码器源码简单分析:解码器主干部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  7. Django 之restfromwork 源码---APIView 分析

    Django 之 djangorestframework的APIView分析 APIView 类中的as_view() 方法 首先 我们从视图函数入手,在urls.py 中的 URLconfig中添加 ...

  8. urllib源码简单分析

    对下面这段代码做分析 import urllib params = urllib.urlencode({'wd': 'python'}) f = urllib.urlopen("http:/ ...

  9. CardboardSDK-iOS 源码简单分析

    该项目地址: 地址 克隆地址为 https://github.com/rsanchezsaez/CardboardSDK-iOS.git 目前如果想在iOS设备上实现双目VR的功能,Google 已经 ...

随机推荐

  1. 【BZOJ】1682: [Usaco2005 Mar]Out of Hay 干草危机(kruskal)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1682 最小生成树裸题.. #include <cstdio> #include < ...

  2. 【BZOJ】1634: [Usaco2007 Jan]Protecting the Flowers 护花(贪心)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1634 贪心.. 我们发现,两个相邻的牛(a和b)哪个先走对其它的牛无影响,但是可以通过 a的破坏花× ...

  3. Excel 经常使用的公式总结

    INDIRECT 返回并显示指定引用的内容.使用INDIRECT函数可引用其他工作簿的名称.工作表名称和单元格引用.   indirect函数对单元格引用的两种 A B C D INDIRECT(&q ...

  4. Numpy库解析

    python中的axis 其实问题理解axis有问题,df.mean其实是在每一行上取所有列的均值,而不是保留每一列的均值.也许简单的来记就是axis=0代表往跨行(down),而axis=1代表跨列 ...

  5. Hadoop1.x目录结构及Eclipse导入Hadoop源码项目

    这是解压hadoop后,hadoop-1.2.1目录 各目录结构及说明: Eclipse导入Hadoop源码项目: 注意:如果没有ant的包可以去网上下,不是hadoop里面的. 然后如果通过以上还报 ...

  6. Unable to instantiate application com.android.tools.fd.runtime.BootstrapApplication 解决办法

    相信很多人都遇到过这个问题,用Android Studio正在运行程序的时候,突然不知道什么原因,报一个找不到application或者找不到activity的错误(java.lang.ClassNo ...

  7. win10怎么关闭把管理员权限

    按住WIN+R 计算机配置----Windows设置----安全设置----本地策略----安全选项----用户账户控制:以管理员批准模式运行所有管理员,把启用改为禁止然后重启电脑

  8. poj_2486 动态规划

    题目大意 N个节点构成一棵树,每个节点上有一个权重val[i], 从根节点root出发在树上行走,行走的时候只能沿着树枝行进.最多在树上走k步,每第一次到达某个节点j,可以获得val[j]的收益,求从 ...

  9. Excel 一个工作表进行按行数拆分

    1. 如下Excel表,总共有120多行数据,如何将以50行数据为一个工作表进行拆分 Sub ZheFenSheet() Dim r, c, i, WJhangshu, WJshu, bt As Lo ...

  10. php文件

    php文件系统函数:  http://www.w3school.com.cn/php/php_ref_filesystem.asp