Django-session中間件源碼簡單分析

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類的內容html

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這裏默認爲Nonedjango

而後經過import_module導入模塊,該方法能夠經過字符串導入模塊cookie

咱們到settings中找到SESSION_ENGINE參數的值session

這裏engine就等於上面導入的模塊dom

而後self.SessionStore = engine.SessionStore,咱們能夠看看engine模塊中都有什麼函數

能夠看到SessionStore就是一個類post

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_keythis

這裏的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值,最後將結果返回

相關文章
相關標籤/搜索