Django:中间件

一、Django中间件

django默认自带七个中间件,还支持用户自定义中间件

1.作用:全局的处理:会对所有的资源请求,所有的返回方式,所有的路由到视图的跳转、所有视图层的异常进行处理

  • 使用场景:当想用全局功能的时候

  • 1:限制访问频率校验:这种功能应该在中间件中编写,减少对django后端资源的浪费

  • 2:用户权限校验:(在web领域:权限就是网址)登录成功之后,每次请求来的时候,在请求中辨别出用户权限,如果符合则可以访问某些功能

2.Django流程图

image-20201026160612127

二、自定义中间件

由于django自带的一些中间件之间是有逻辑联系的,所有自定义的中间件最好注册在最开头或者结尾,不要插在七个自定义的中间件中间

1.创建存储自定义中间件代码的py文件或者目录

2.参考自带中间件的代码编写类并继承MiddlewareMixin

3.在类中编写五个可以自定义中间件的方法

  • 需要掌握的
process_request
process_response
  • 需要了解的
process_view
process_exception
process_template_response

4.在配置文件中需要注册才能生效

三、中间件执行流程

1.request和response通过中间件的顺序

image-20221221212020829

image-20221221120823960

2.process_request和process_response方法

  • process_request

    1.请求来的时候会依次经过每一个在settings中注册了的中间件里面的该方法,在注册顺序从上往下依次执行,每个中间件中的process_request,如果没有process_request方法则跳过

    2.如果方法自己返回了HttpReponse对象,那么不再往后执行,直接原路返回给前端

class MyMiddleware1(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("我是中间件1的view")
  • process_response

    1.响应走的时候,会从下往上一次经过每一个注册了的中间件里面的方法,如果没有则直接跳过

    2.该方法必须要有两个形参requestresponse,返回形参response,也可以返回自己定义的

如果在执行process_response方法的时候,直接返回了HttpReponse对象,那么会原路返回执行process_response

四、django中间件三个了解的方法

  • 需要了解的
process_view
process_exception
process_template_response

1.process_view

触发时间路由匹配成功之后,执行视图函数/类之前自动触发(顺序同process_request,在注册文件中从上往下依次触发)

class MyMiddleware01(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("from MyMiddleware0101 process_view")

image-20221222091854717

2.process_exception

触发时间:执行视图函数/类,报错的之后自动触发,执行顺序同process_response从下往上

形参exception是异常信息

class MyMiddleware1(MiddlewareMixin):

    def process_exception(self, request, exception):
        print("from MyMiddleware01 process_exception")

3.process_template_response

只要形参中有response的都需要返回

触发时间:当视图函数/类中返回一个HttpResponse对象,并且对应一个方法的时候自动触发(执行顺序同process_response从下往上)

五、基于django中间件的插拔式设计

django中间件管理功能的方式:

将各个功能制作成配置文件的字符串形式,如果想拥有该功能就编写对应的字符串,如果不想使用该功能则注释掉对应的字符串

1.内置模块importlib

作用:通过传一个字符串形式的路径,将字符串导入模块

原理:通过字符串按.从右往左切一个,前面是路径,最后一个是py文件,从而导入模块

只要能变成字符串,就可以和用户交互

缺点:字符串的结尾,最小单位只能是py文件级别,不能是py文件中的变量名

import importlib
s1 = 'bbb.b'  # aaa.bbb.ccc.b
res = importlib.import_module(s1)  # from aaa.bbb.ccc import b
print(res)  # <module 'bbb.b' from 'D:\\pythonProject03\\djangomiddle\\bbb\\b.py'>

2.插拔式设计

  • 需求与分析:编写一个消息通知功能,通过微信、qq、邮箱,点一个按钮

(1)方式一:函数封装版本

通过每个函数来进行封装发送消息的功能

def send_qq(msg):
    print('来自于qq的消息>>>:%s'%msg)
    
def send_weixin(msg):
    print('来自于weixin的消息>>>:%s'%msg)    
    
def send_email(msg):
    print('来自于email的消息>>>:%s'%msg)    

(2)方式二:基于django中间件的功能设计

多态性:不同的对象具有相同功能,应该有相同名字的方法

  • 定义三个py文件存储不同的类
# qq.py
class Qq(object):
    def __init__(self):
        pass  # 模拟发送qq之前需要做的准备操作

    def send_msg(self, content):
        print('来自于qq的消息>>>:%s'%content)
# Weixin.py
class Weixin(object):
    def __init__(self):
        pass  # 模拟发送微信之前需要做的准备操作

    def send_msg(self, content):
        print('来自于Weixin的消息>>>:%s' % content) 
# Email.py
class Email(object):
    def __init__(self):
        pass  # 模拟发送email之前需要做的准备操作

    def send_msg(self, content):
        print('来自于email的消息>>>:%s' % content)  
  • 在notify目录的__init__.py文件中,应该导入三个模块中的类,

    运用 importlib模块拿到文件名的字符串与类的字符串,最后通过反射获取字符串对应的类产生对象,调用发送消息的方法

from settings import NOTIFY_LIST
import importlib


def send_all(content):
    for full_path in NOTIFY_LIST:  # path='notify.email.Email'
        module_path, class_str_name = full_path.rsplit('.', maxsplit=1)  # 'notify.email' 'Email'
        # 1.利用字符串导入模块 拿到模块名
        module_name = importlib.import_module(module_path)  # from notify import email
        # 2.利用反射从模块中获取字符串对应的类名
        class_name = getattr(module_name, class_str_name)  # Email
        # 3.利用类名加括号产生对象
        obj = class_name()
        # 4.对象调用发送消息的方法
        obj.send_msg(content)
  • settings.py文件中注册发送消息的类
NOTIFY_LIST = [
    'notify.email.Email',
    'notify.qq.Qq',
    'notify.weixin.Weixin',
]

六、cookie与session简介

1.Http协议四大特性

image-20221222104508475

2.cookie与session简介

(1)历史发展

早期:早期的cookie很不安全,可以通过查看浏览器中的文件就可以查看到用户名和密码

现在:当第一次输入用户名和密码之后,服务端会返回一个随机字符串,在服务端内部已经记录了随机字符串和用户的对应关系,当发送请求的时候,就可以进行比对随机字符串。

(2)概念

session:用户登录的时候返回一个随机字符串,随机字符串保存在服务端上和用户状态相关的数据就叫session

cookie:保存在客户端上和用户状态相关的数据叫做cookie

名称 存储位置
Cookie 客户端/浏览器
Session 服务端
  • session的工作需要依赖于cookie所有与用户状态相关的操作都需要依赖于cookie

image-20221222105804021

(3)补充

  • 谁拿到cookie的用户数据,通过cookie请求服务端,谁就是那个用户,所以在web领域没有绝对的安全

  • 浏览器也有资格拒绝保存服务端发送过来的cookie

七、django操作cookie

1.cookie与三板斧

操作cookie就不能直接返回HttpResponse对象,需要我们先拿到HttpResponse用变量接收

# render
obj1 = render()
return obj1

# HttpResponse
obj2 = HttpResponse()
return obj2

# redirect
obj3 = redirect()
return obj3

2.关于Cookie的方法

# 设置Cookie
def cookie_test(request):
    # 浏览器向该地址发一个请求,就在浏览器写入 name = duoduo
    obj = HttpResponse('ok')
    obj.set_cookie('name', 'duoduo')    # 写入到浏览器了,在http响应头里:cookie: name=Darker
    obj.set_cookie('age', '18')         # 写入到浏览器了,在http响应头里:cookie: name=Darker
    return obj
    
    
# 取出Cookie
def get_cookie(request):
    print(request.COOKIES)
    print(request.COOKIES.get('name'))
    return HttpResponse(f'我拿到了你的cookie,name是:{request.COOKIES.get("name")} ,age是:{request.COOKIES.get("age")}')
  • path 只获取用户输入的路由信息
  • path_info 只获取用户输入的路由信息
  • get_full_path() 获取用户输入的路由信息+ 问号后面携带的数据

3.基于cookie编写用户登录注册功能

(1)初级版本

def login_func(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'duoduo' and password == '123':
            obj = redirect('/home/')
            obj.set_cookie('name', username)
            return obj
    return render(request, 'loginPage.html')

  • 校验登录的装饰器
def login_auth(func_name):
    def inner(request, *args, **kwargs):
      # 拿到cookie
        if request.COOKIES.get('name'):
            res = func_name(request, *args, **kwargs)
            return res
        return redirect('/login/')
    return inner

(2)进阶版本

用户没有登录之前想访问某个网站,在输入用户名密码之后,就应该跳转回之前的网站

def login_func(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'jason' and password == '123':
            target_path = request.GET.get('next')
            if target_path:
                obj = redirect(target_path)
            else:
                obj = redirect('/home/')
            obj.set_cookie('name', username)
            return obj
    return render(request, 'loginPage.html')
  • 校验用户登录的装饰器
def login_auth(func_name):
    def inner(request, *args, **kwargs):
        # request.path_info 只获取用户输入的路由信息
        target_path = request.path_info
				# 拿到cookie
        if request.COOKIES.get('name'):
            res = func_name(request, *args, **kwargs)
            return res
        return redirect('/login/?next=%s' % target_path)
    return inner

八、django操作session

由于session是保存在服务端的用户相关的数据,就因该有个地方存储session,保存数据都在数据库中,所以当我们执行迁移命令的时候,Django就会自动帮我们创建django_session的表,用来存储session

  • 如图,执行迁移命令后,django自动创建的session表

image-20221222121149618

django默认的的session失效时间是14天

1.设置session

request.session['key'] = value

(1)生成一个随机字符串

(2)对value数据做加密处理,在django_session表中存储

  • 随机字符串 :也就是加密的数据

(3)将随机字符串也发送一份给客户端cookied保存

  • sessionid :随机字符串

2.获取session

request.session.get('key')

(1)自动获取随机字符串

(2)去Django_session表中根据随机字符串获取加密的数据

(3)自动解密数据并处理到request.session.get()中

3.补充

1.session可以设置过期时间

2.存储session数据的位置也可以修改

posted @ 2022-12-21 21:26  Duosg  阅读(234)  评论(0编辑  收藏  举报