Django 中间件 & cookie & session

一、中间件

1.1 中间件的使用

  1. 编写类
  2. 在settings中注册

1.编写类

from django.utils.deprecation import MiddlewareMixin

class MyMiddleware(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

2.在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',
    'utls.my_mw.My1Middleware',
    'utls.my_mw.My2Middleware',
    'utls.my_mw.My3Middleware',
]
# 动态导入 + 反射

注意:
python执行顺序是从上往下执行, 所以,定义的中间件越靠前,就越先执行。如果自己定义的中间件放在最前面,则优先于系统定义的中间件执行,可能会导致某些操作出错,例如:

'utls.my_mw.My1Middleware',
'django.contrib.sessions.middleware.SessionMiddleware',

如果在系统定义的session之前定义自己的中间件,那么在中间件中,如果使用到了request.session,那么就会报错。

3. 测试:

a.正常请求:

在这里插入图片描述

  1. 请求从浏览器发出,首先经过wsgi进行封装,才进入Django程序
  2. 一个请求需要经过所有的中间件,每一个中间件可以看做一个类,首先执行类中的process_request方法。
  3. 执行完最后一个中间件的process_request方法后,才会进入路由匹配。
  4. 路由匹配完成,又从第一个中间件开始,执行所有中间件的process_view方法
  5. 执行完成所有中间件的process_view方法后,才会进入视图函数,得到HttpResponse,再返回。
  6. 返回时,也需要执行所有中间件的process_response 方法,然后才返回给路由器。
# my_wm.py
from django.utils.deprecation import MiddlewareMixin


class My1Middleware(MiddlewareMixin):
    def process_request(self, request):
        print('1111 process_request')

    def process_view(self, request, view, *args, **kwargs):
        print('1111 process_view')

    def process_response(self, request, resposne):
        print('1111 process_response')
        return resposne


class My2Middleware(MiddlewareMixin):
    def process_request(self, request):
        print('2222 process_request')

    def process_view(self, request, view, *args, **kwargs):
        print('2222 process_view')

    def process_response(self, request, resposne):
        print('2222 process_response')
        return resposne


class My3Middleware(MiddlewareMixin):
    def process_request(self, request):
        print('3333 process_request')

    def process_view(self, request, view, *args, **kwargs):
        print('3333 process_view')

    def process_response(self, request, resposne):
        print('3333 process_response')
        return resposne

# views.py
class MineView(View):
    def get(self, request, v1):
        print('进入视图函数,执行函数')
        return HttpResponse('11')
# urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
 	path('mine/<int:v1>/', views.MineView.as_view()),
]

在这里插入图片描述

b非正常请求:

请求被某个process_request拦截
在这里插入图片描述

  1. 请求从浏览器发出,首先经过wsgi进行封装,才进入Django程序
  2. 请求经过某个process_request方法,被拦截,直接返回当前中间件的process_response方法
  3. 从该中间件向前执行所有的process_response方法,返回给路由器结果。
b1.在process_request中,请求被阻塞

例如,当访问某个链接,规定好,需要携带token=12345才允许访问
修改process_request

class My2Middleware(MiddlewareMixin):
    def process_request(self, request):
        if request.path_info == "/x3/":
            token = request.GET.get('token')
            if token == "12345":
                return
            else:
                return HttpResponse("无权访问")

如果直接访问 /x3就会提时无权访问
在这里插入图片描述

注意点:此时还没有定义/x3/这个路由,但是仍然不报错,因为请求不会到达路由匹配,所以即使不定义路由也没关系

如果访问/x3/?token=12345,才会执行相应的函数(前提是定义好了x3视图函数,并且在urls添加了)

b2请求被某个process_view拦截

在这里插入图片描述

  1. 请求从浏览器发出,首先经过wsgi进行封装,才进入Django程序
  2. 一个请求需要经过所有的中间件,每一个中间件可以看做一个类,首先执行类中的process_request方法。
  3. 执行完最后一个中间件的process_request方法后,才会进入路由匹配。
  4. 路由匹配完成,又从第一个中间件开始,执行所有中间件的process_view方法
  5. 执行某个process_view方法,被拦截,直接跳到最后一个中间件,执行process_response
  6. 返回时,也需要执行所有中间件的process_response 方法,然后才返回给路由器。
    在这里插入图片描述
    关于自定义prcess_response,一般用于对请求要返回的数据进行修改,条件headers,对response加密返回等。
def process_response(self, request, response):
        response["city"] = "CD"
        return response

4.不常用的中间件方法

中间件在类型定义中,除了常见的process_requestprocess_viewprocess_response外,还有process_exceptionprocess_template_response

process_exception,视图函数有异常,处理出现异常时
process_template_response,对于视图函数返回内容渲染扩展。
	- 在视图函数中如果返回的对象内部有一个render方法且可以被调用执行
	- process_template_response返回response参数(返回值)
	- 在自定义的MyReponse的render方法中必须返回HttpRespose

1.2 源码*

1.关于请求

最开始实例化Handler对象,执行一次__init__方法,然后监听8000端口,有请求到来,就执行对象中的__call__方法

from wsgiref.simple_server import make_server

class Handler:
    
    def __init__(self):
        # 做一些初始化动作
        self.name = "xxx"
        
	
    def __call__(self,environ, start_response):
        # 根据初始化的动作,去执行...
        # ...
        start_response('200 OK', [('Content-Type', 'text/html')])
    	return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]


if __name__ == '__main__':
    obj = Handler() # 执行 
    httpd = make_server('127.0.0.1', 8000, obj)  # 有请求到来时,执行 obj(environ, start_response)
    httpd.serve_forever()

在这里插入图片描述

2. 启动Django项目,默认执行WSGIHandler.init

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_middleware()
    def load_middleware(self, is_async=False):
		### 定义3个列表
		# 用于存放process_view
        self._view_middleware = []
        # 用于存放 process_template_response
        self._template_response_middleware = []
        # 用于存放 process_exception
        self._exception_middleware = []
		
		# 暂不考虑异步操作,get_response = self._get_response
        get_response = self._get_response_async if is_async else self._get_response
       
		#闭包,handler=函数,handler() -> get_resposne() 	
        handler = convert_exception_to_response(get_response)
        handler_is_async = is_async
        # 获取settings的中间件,反转
        for middleware_path in reversed(settings.MIDDLEWARE):
        	## 根据字符串导入模块。找到utls.my_wm 下的类 MyMiddleware
            middleware = import_string(middleware_path)
            ...
           	#省略异步操作
           	...
            try:
                # 再次封装,如果handelr不是异步,adapted_handler = handler
                adapted_handler = self.adapt_method_mode(
                    middleware_is_async,
                    handler,
                    handler_is_async,
                    debug=settings.DEBUG,
                    name="middleware %s" % middleware_path,
                )
                # 类()->实例化类
                # mw_instance = middlerware(handler)
                mw_instance = middleware(adapted_handler)
           		 ...
           		# handler = handler
                handler = adapted_handler
			# 去每个中间件找,有process_view,就在列表最前面,添加该方法
            if hasattr(mw_instance, "process_view"):
                self._view_middleware.insert(
                    0,
                    self.adapt_method_mode(is_async, mw_instance.process_view),
                )
            # 去每个中间件找,有process_template_response,就在列表最前面,添加该方法
            if hasattr(mw_instance, "process_template_response"):
                ...
            if hasattr(mw_instance, "process_exception"):
                ...
			
            handler = convert_exception_to_response(mw_instance)
            handler_is_async = middleware_is_async

        # Adapt the top of the stack, if needed.
        handler = self.adapt_method_mode(is_async, handler, handler_is_async)
        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler

在这里插入图片描述
在这里插入图片描述
层层封装,每个get_resposne都是下一个中间件对象,调用函数,get_resposne(),等同于执行对象的__call__方法,而每个中间件的__call__方法都包含有process_request,get_response,process_resoponse直到执行到最后一个中间件的_get_resposne()方法,进行路由匹配+执行视图函数

3.请求到来WSGIHandler.call

   def __call__(self, environ, start_response):
        ...
        # 封装request
        request = self.request_class(environ)
		# 中间件+路由匹配+视图函数 ->得到HttpResponse对象
        response = self.get_response(request)
		...
        return response

核心就是 response = self.get_response(request)

    def get_response(self, request):
       	...
       	# 从init可以得到 self._middleware_chain = handler
       	# 1.将所有中间件的process_request方法按照注册顺序,从前往后执行了一遍
       	#2.执行self._get_response --->路由匹配
       	#3.将所有中间件的process_response方法按照注册顺序,从后往前执行了一遍
        response = self._middleware_chain(request)
 		...
        return response
    def _get_response(self, request):
       
        response = None
        # 解析请求,获取路径和参数
        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
            )
            # 如果view方法有返回,那么就直接结束,不再执行后面的
            if response:
                break

        if response is None:
        	# 如果返回值为空,再次封装一下callback,
        	# 1.判断异步,异步操作的处理,暂时先不管。
        	# 2. django内部数据库支持的事务
            wrapped_callback = self.make_view_atomic(callback)
            try:
            # 执行视图函数
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            # 视图函数出错,就会找中间件中定义的process_exception方法,去执行
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        # 检查返回结果,
        self.check_response(response, callback)

        #如果返回结果含有render函数,且render是可执行的,那么就会执行process_template_response方法
        if hasattr(response, "render") and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                # Complain if the template response middleware returned None
                # (a common error).
                self.check_response(
                    response,
                    middleware_method,
                    name="%s.process_template_response"
                    % (middleware_method.__self__.__class__.__name__,),
                )
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        return response
  1. 先执行所有的process_request
  2. 执行self._get_response
    -路由匹配
    -中间件process_view
    -视图函数
    -中间件的process_exception
    -中间件的process_template_response
# 核心
# handler = SecurityMiddleware对象
#             __call__
#                process_request
#                get_reponse = SessionMiddleware对象
#                process_response 
#                              __call__
#                                   process_request 
#                                   get_reponse = CommonMiddleware对象
#                                   process_response
#                                                 __call__
#                                                     process_request
#                                                     get_reponse = MyMiddleware对象
#                                                     process_response

从源码看出执行流程:

  1. 每一个对象执行__call__,都是在执行三种方法
  2. 先执行 process_request
  3. 然后执行get_response,每个对象的get_response都是下一个定义的中间件的对象
  4. 执行对象,又执行对象的__call__,循环下去直到执行到最后一个对象的get_response
  5. 最后一个对象执行get_response是执行_get_response,执行视图函数+process_view +异常处理
  6. 全部完成后,又从最后的process_response开始往前直行

二、COOKIE

Cookie本质上就是存储在浏览器上的一堆键值对
在这里插入图片描述

from django.urls import path
from django.shortcuts import HttpResponse


def x1(request):
    # 包含:响应体、响应头、状态码等信息
    obj = HttpResponse("x1", status=201, reason="OK")
    # 设置响应头
    obj['city'] = "ChengDu"

    # 设置cookie
    # max_age=10: 设置超时时间为10s的cookie
    # path="/": 设置作用域为当前域名所有路径
    obj.set_cookie("v1", "root", max_age=10, path="/")
    obj.set_cookie("v2", "hello")
    # 设置签名的cookie
    obj.set_signed_cookie("name", "szr")  
    return obj


def x2(request):
    print(request.COOKIES)
    xx = request.get_signed_cookie("name")  # 用户不能自己修改cookie的内容,但凡有一点修改,都不能校验通过。
    print(xx)
	# 获取cookie
    # print(request.COOKIES.get('v2'))
    return HttpResponse("x2")


urlpatterns = [
    path('x1/', x1, name='x1'),
    path('x2/', x2, name='x2'),
]

三、Session

Session的使用一般依赖于Cookie,将一些数据不再发送值浏览器,而是保存的后端的服务器上。
在这里插入图片描述

3.1 使用

Session到底要存储位置,默认存在数据库。

INSTALLED_APPS = [
    'django.contrib.admin',
	...
    # 'django.contrib.sessions',  # sessions功能的APP    django_session
   ...
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware', # process_request/process_response
    ...
]
# global_settings.py
############
# SESSIONS #
############
# Session存储位置吗,默认db
SESSION_ENGINE = "django.contrib.sessions.backends.db"

# 如果存储到文件中,文件的路径。
SESSION_ENGINE = "django.contrib.sessions.backends.file"
SESSION_FILE_PATH = None

# 存储到缓存
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"


# 存储到缓存 + 数据库
SESSION_ENGINE = "django.contrib.sessions.backends.cache_db"
SESSION_CACHE_ALIAS = "default"

# 存储到cookie
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

# class to serialize session data
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"


# -------------------------------其他一些可设置
# Cookie name. This can be whatever you want.
SESSION_COOKIE_NAME = "sessionid"
# Age of cookie, in seconds (default: 2 weeks).
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
# A string like "example.com", or None for standard domain cookie.
SESSION_COOKIE_DOMAIN = None
# Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_SECURE = False
# The path of the session cookie.
SESSION_COOKIE_PATH = "/"
# Whether to use the HttpOnly flag.
SESSION_COOKIE_HTTPONLY = True
# Whether to set the flag restricting cookie leaks on cross-site requests.
# This can be 'Lax', 'Strict', 'None', or False to disable the flag.
SESSION_COOKIE_SAMESITE = "Lax"
# Whether to save the session data on every request.
SESSION_SAVE_EVERY_REQUEST = False
# Whether a user's session cookie expires when the web browser is closed.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
  • 请求时,生成Session、Cookie。
  • 再次请求,默认携带cookie,根据Cookie中的凭证,去找到Session中原本存储的数据。

3.2 引擎配置

  • 数据库引擎
   SESSION_ENGINE = "django.contrib.sessions.backends.db"
      INSTALLED_APPS = [
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
      ]
  >>>python manage.py makemigrations
  >>>python manage.py migrate
  • 文件
  # 如果存储到文件中,文件的路径。
  SESSION_ENGINE = "django.contrib.sessions.backends.file"
  SESSION_FILE_PATH = None
  INSTALLED_APPS = [
      #'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
  ]

3.3中间件

1.创建对象

在启动django程序时,会自动创建 SessionMiddlewared对象。

class MiddlewareMixin:

    def __init__(self, get_response):
        self.get_response = get_response

class SessionMiddleware(MiddlewareMixin):
    def __init__(self, get_response):
        super().__init__(get_response)

        # "django.contrib.sessions.backends.db"   "django.contrib.sessions.backends.file"
        engine = import_module(settings.SESSION_ENGINE)

        # db.SessionStore    file.SessionStore
        self.SessionStore = engine.SessionStore
2 请求到来 *
def process_request(self, request):
    # 1.去Cookie中读取凭证 sessionid="xxxx"
    session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
    # 2.实例化
    request.session = self.SessionStore(session_key)

def x1(request):
    request.session['id'] = 1
    request.session['city'] = 'cd'    #调用类中的__setitem__

    return HttpResponse("x1")
3 请求结束*
# 将缓存中的数据保存,
#如果session引擎是文件,就保存文件中。如果是db,就保存数据库中
request.session.save()
...
# 将session写入response中
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,
    samesite=settings.SESSION_COOKIE_SAMESITE,
)

总结

  • 使用角度:
    • 中间件
    • Cookie
    • Session:Cookie + 中间件
  • 源码流程:
    • 中间件、cookie、session
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值