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

1.process_view

​ process_view方法是在Django路由系统之后(路由匹配成功之后),视图系统之前(执行视图函数/类之前)执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的

2.process_exception

​ 视图函数/类执行报错自动触发(触发顺序是根据注册顺序从后往前执行)

3.process_template_response

​ 视图函数/类返回的HttpResponse对象含有render属性(需要在内部定义一个render函数,并且要把这个函数名称绑定给render属性)并且对应一个方法的时候自动触发(触发顺序是根据注册顺序从后往前执行)

代码如下(用的不多不用深究):

views.py

from django.shortcuts import render, HttpResponse, redirect

# Create your views here.
def index_func(request):
print('from index view') def render():
return HttpResponse('我是一个大奇葩') obj = HttpResponse('index view')
obj.render = render
return obj

这里是自定义的中间件的代码

from django.utils.deprecation import MiddlewareMixin

class MyMiddleWare01(MiddlewareMixin):
def process_request(self, request):
print('from MyMiddleWare01 process_request') def process_response(self, request, response):
print('from MyMiddleWare01 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
# print(view_func,view_args,view_kwargs)
print('from MyMiddleWare01 process_view') def process_exception(self, request, exception):
print(exception)
print('from MyMiddleWare01 process_exception') def process_template_response(self,request,response):
print('from MyMiddleWare01 process_template_response')
return response class MyMiddleWare02(MiddlewareMixin):
def process_request(self, request):
print('from MyMiddleWare02 process_request') def process_response(self, request, response):
print('from MyMiddleWare02 process_response')
return response def process_view(self, request, view_func, view_args, view_kwargs):
print('from MyMiddleWare02 process_view') def process_exception(self, request, exception):
print(exception)
print('from MyMiddleWare02 process_exception') def process_template_response(self,request,response):
print('from MyMiddleWare02 process_template_response')
return response

结果如图:

二、django中间件五个方法的执行流程详解

上一部分,我们了解了中间件中的5个方法,它们的参数、返回值以及什么时候执行,现在总结一下中间件的执行流程。

请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法(注意不是掉头执行所有的process_response方法),将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。

process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。假如中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。

process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下:

三、基于django中间件的功能设计

功能设计介绍

在使用自定义中间件的时候我们体会到了通过注册来启动或关闭一个功能的方便,我们称为功能的插拔式设计.

使用方式介绍:

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

如何利用字符串导入模块

这里我们使用importlib模块来模仿django中间件的调用功能的方式.

代码演示如下:

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'>
'''注意字符串的结尾最小单位只能是py文件 不能是py文件里面的变量名'''
思考django中间件是如何处理的(最小单位是类名)

功能模拟

需求分析

​ 模拟编写一个消息通知功能(微信、qq、邮箱)

实现方式1:基于函数封装的版本

​ 使用函数封装三个发送消息的应用(其实就写简单一些直接上print打印一句话即可)

​ 然后再类似之前写atm的时候一样用一个启动函数给他运行

​ 如果我们不想执行某个应用的消息通知功能,就把这个功能在启动函数中对应的调用代码注释掉即可.

​ 但是这种使用发那个时没有眼前一亮的感觉,很一般.

实现方式2:基于django中间件的功能设计

首先是使用软件开发目录规范创建文件,将功能分到不同的接口层中去然后写上对应的代码(都是简单模拟)

接下去我们在配置文件settings中模仿这写注册表,同时具体到py文件内部的类名(django也是具体到类名)

在配置文件中注册后我们想到在软件开发目录中我们是把这些接口层的文件当模块导入调用的,而当我们把这个接口层的文件夹当成包导入的时候会更加方便(调包的时候其实就是在掉他内部的双下init文件)

这时候我们通过查看Django模块的配置文件导入中间件的方式,可以看到他在配置文件中注册的信息其实是具体到类的,但是importlib只能具体到py文件,这时候我们想到用split方法切割字符串.

基于上面的这些思考,我们在双下init文件中定义函数设置调用的模块的函数,然后导入配置文件中的配置信息,通过配置信息获取要执行的应用做代表的功能,然后通过切割字符串获取需要执行的功能类,然后创建对象执行功能,这时候我们就实现了通过更改配置信息中的注册表修改功能的执行与否.

评价:眼前一亮,回味无穷!

四、cookie与session简介

知识点回顾

回忆:HTTP协议四大特性

1.基于请求响应

2.基于TCP、IP作用于应用层之上的协议

3.无状态

不保存客户端的状态

4.无连接

需求变化分析

最开始的网站都不需要用户注册 所有人来访问获取到的数据都是一样的

随着互联网的发展很多网站需要指定当前用户的状态,因此需要用到cookie和session

cookie

​ 保存在客户端,存储与用户状态相关的信息

session

​ 保存在服务端,存储与用户状态相关的信息

ps:session的工作需要依赖于cookie

补充:浏览器有资格拒绝保存服务端发送过来的cookie数据(需要在设置里设置,但都拒绝的话会出现不能登陆的情况)

我们可以在浏览器中右键检查查看cookies信息,如果我们主动删除这些cookies信息就需要重新登陆,如果短时间频繁操作,可能被某些网页检测到会封你ip(有人上课演示被封了,我也不知道是谁).

五、django操作cookie

要想操作cookie就不能直接返回HttpResponse对象 必须先用变量接收,因为我们如果要操作cookie就需要对三板斧产生的对象进行操作

obj1 = render()
return obj1
obj2 = HttpResponse()
return obj2
obj3 = redirect()
return obj3

首先实现一个本地Cookie的简单操作 通过后端传给Cookie 随时随地可以调用

views
def set_cookie(request):
obj = HttpResponse('Happy Happy !!!')
obj.set_cookie('name', 'James')
return obj

这里我们可以看到set_cookie方法把内部的数据发送到前端转换成了cookie.

同时我们可以在后端用request.COOKIES获取cookie信息

再来写一个登录账号跳转页面的情况吧!
这里的装饰器是用于判断cookie所代表的信息是否正确
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):
# print(request.path) # 只获取用户输入的路由信息
# print(request.path_info) # 只获取用户输入的路由信息
target_path = request.path_info
# print(request.get_full_path()) # 获取用户输入的路由信息+问号后面携带的数据
if request.COOKIES.get('name'):
res = func_name(request, *args, **kwargs)
return res
return redirect('/login/?next=%s' % target_path) return inner @login_auth
def home_func(request):
return HttpResponse('home页面 只有登录的用户才可以查看') @login_auth
def home1_func(request):
return HttpResponse('home1页面 只有登录的用户才可以查看') @login_auth
def home2_func(request):
return HttpResponse('home2页面 只有登录的用户才可以查看')

在上面的代码中我们又联系实际情况,想到了一个问题,做的好一些的网站,通常都是会 记住我们之前访问的网页,在进行登陆有继续访问该网页.这里就需要用到一些小知识点了.也就是我们需要在装饰器中修改跳转的内容让他记住我们之前想要访问的网址.

def login_auth(func_name):
def inner(request, *args, **kwargs):
# print(request.path) # 只获取用户输入的路由信息
# print(request.path_info) # 只获取用户输入的路由信息
target_path = request.path_info
# print(request.get_full_path()) # 获取用户输入的路由信息+问号后面携带的数据
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是保存在服务端上面的数据 就应该有个地方能够存储

我们只需要执行数据库迁移命令即可 django会自动创建很多需要的表

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

设置session的代码:

request.session['key'] = value

当我们在设置session的时候会执行下列操作:

  • 1.生成一个随机字符串
  • 2.对value数据做加密处理 并在django_session表中存储

    随机字符串>>>加密数据
  • 3.将随机字符串也发送一份给客户端保存(cookie)

    sessionid:随机字符串

获取session的代码

request.session.get('key')

在获取session的时候会执行下列操作

  • 1.自动获取随机字符串
  • 2.去django_session表中根据随机字符串获取加密的数据
  • 3.自动解密数据并处理到request.sesion.get()中

总结和补充

总结

通过上面的知识点讲解我们可以发现session的使用非常简单,但是在底层来看,其实代码执行了非常多的操作,今后我们写代码也应该朝这方面靠拢.

补充说明

当我们在执行代码进行实操的时候会遇到一些问题

设置session中的注意事项

def set_session(request):
request.session['name'] = 'Like' # 设置Session
return HttpResponse('设置Session')
'''
第一次运行的时候访问接口会发生报错 no such table: django_session
因为当客户端登录成功之后 服务端会产生一个随机字符串 返回客户端去保存(之所以要保存就是因为我们只有保存了这个字符串对应的信息,才能去进行信息校对,否则前端发送cookie我们无法识别)
服务端会针对这些字符串跟对应用户信息做一个保存 这个关系就保存在服务端执行迁移命令里面的Django_session表里面(但是目前我们第一次跑程序还没执行迁移命令)
表里面有三个字段 Session_key Session_data Expire_date
session_ky 随机产生的一个加密字符串令牌
session_data session数据
expire——date django默认的session失效时间14天(不登录) 这个时候迁移命令创建完之后就可以访问接口了
'''

迁移操作截图

获取session中的注意事项


def get_session(request): # 获取session
print(request.session.get('name'))
return HttpResponse('获取Session') # 自动会加密解密 '''
request.session['name'] = 'Like'
1.django自动产生一个随机字符串返回给客户端(对name加密)
2.往django_session创建数据(对jason加密)
request.session.get('name')
1.自动从请求中回去sessionid对应的随机字符串
2.拿着随机字符串去django_session中匹配数据
3.如果匹配上还会自动解密数据并展示
'''

其他注意事项

session的存储位置可以有五种模式(数据库、缓存数据库、文件、缓存+数据库、加密)

1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 其他公用设置项:
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) session其他操作
'''
删除当前会话的所有Session数据
request.session.delete()
# 删除当前的会话数据并删除会话的Cookie。
request.session.flush()
# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
'''

七、作业

1.编写一个session版本的用户登录功能

装饰器、自动记忆跳转、过期时间

views

from django.shortcuts import render, redirect, HttpResponse
from django import forms
from app01 import models
from django.views.decorators.csrf import csrf_exempt, csrf_protect # Create your views here. class Login(forms.Form):
username = forms.CharField(max_length=16, min_length=2,
# widget=forms.widgets.TextInput(),
error_messages={
'max_length': "用户名长了",
'min_length': "用户名短了",
}
) # 字符字段,限定最大最小长度
password = forms.CharField(max_length=16, min_length=6,
widget=forms.widgets.PasswordInput(),
error_messages={
'max_length': "密码长了",
'min_length': "密码短了",
}
)
# 用forms组件校验数据,这里是用全局钩子判断用户名跟密码是否正确
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user_obj = models.User.objects.filter(username=username).first()
if not user_obj:
self.add_error('username', '用户名不存在')
elif not user_obj.password == password:
self.add_error('password', '密码不正确')
return self.cleaned_data # 定义装饰器判断用户的登陆状态(如果有cookie就是登陆过了,如果没有就记住当前需要访问的网址在登陆后用于自动跳转)
def login_auth(func):
def inner(request, *args, **kwargs): # 视图函数一定含request参数,所以单独先接收便于操作
target_path = request.path_info # 拿到请求的路由
username = request.session.get('key') # 拿到登录态
# username = request.COOKIES.get('name')
if username: # 校验用户态
res = func(request, *args, **kwargs) # 用户登录态则直接返回原本访问的网页
return res
return redirect(f'/login/?next={target_path}') # 将被装饰的视图函数的路由发送给login函数 return inner # 这是让csrf策略不要检测的装饰器
@csrf_exempt
def login_func(request):
login_obj = Login()
if request.method == 'POST': # 提交表单打算登录
login_obj = Login(request.POST)
if login_obj.is_valid(): # 数据有效且密码校验也正确
if not request.GET.get('next'):
obj = redirect('/home/') # 如果不是通过装饰器重定向来的,就重定向到首页
else:
obj = redirect(request.GET.get("next")) # 有next就重定向到原本的网页
request.session['key'] = login_obj.cleaned_data.get('username') # 保存登录态
request.session.set_expiry(3000)
return obj
return render(request, 'loginPage.html', locals()) # get请求拿登录界面 # 主页需要登陆后访问,用装饰器来实现需求
@login_auth
def homePage_func(request):
return render(request, 'homePage.html')

models

记得换成MySQL数据库

from django.db import models

# Create your models here.
class User(models.Model):
username = models.CharField(max_length=16, verbose_name='用户名')
password = models.CharField(max_length=16, verbose_name='密码')

urls

urlpatterns = [
path('admin/', admin.site.urls),
path('home/', views.homePage_func, name='homePage'),
path('login/', views.login_func, name='login'),
]

12月22日内容总结——django中间件的三个了解要求的方法、基于django中间件的功能设计、cookie与session的更多相关文章

  1. 12月22日《奥威Power-BI财务报表数据填报》腾讯课堂开课啦

    一扇可以通向任何地方的“任意门”,是我们多少人幼时最梦寐以求的道具之一.即使到了现在,工作中的我们还会时不时有“世界那么大,我想去看看”的念头,或者在突然不想工作的时刻,幻想着自己的家门变成了“任意门 ...

  2. 2016年12月22日 星期四 --出埃及记 Exodus 21:17

    2016年12月22日 星期四 --出埃及记 Exodus 21:17 "Anyone who curses his father or mother must be put to deat ...

  3. 北京Uber优步司机奖励政策(12月22日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  4. 12月22日 update_columns,完成第9节。

    Update_columns(attributes) //等同于update_column 直接更新database. 使用UPdate SQL 语法. ⚠️ :忽略了validations, Cal ...

  5. 2015年12月10日 spring初级知识讲解(三)Spring消息之activeMQ消息队列

    基础 JMS消息 一.下载ActiveMQ并安装 地址:http://activemq.apache.org/ 最新版本:5.13.0 下载完后解压缩到本地硬盘中,解压目录中activemq-core ...

  6. 22.Express框架——2019年12月19日

    2019年12月19日14:16:36 1. express简介 1.1 介绍 Express框架是后台的Node框架,所以和jQuery.zepto.yui.bootstrap都不一个东西. Exp ...

  7. 2016年12月27日 星期二 --出埃及记 Exodus 21:22

    2016年12月27日 星期二 --出埃及记 Exodus 21:22 "If men who are fighting hit a pregnant woman and she gives ...

  8. 2016年12月1日 星期四 --出埃及记 Exodus 20:22

    2016年12月1日 星期四 --出埃及记 Exodus 20:22 Then the LORD said to Moses, "Tell the Israelites this: `You ...

  9. Autodesk 最新开发技术研讨会 -8月22日-Autodesk北京办公室

    为了增进与广大中国地区Autodesk产品的二次开发人员的了解与互动,帮助中国地区的Autodesk产品二次开发人员了解Autodesk最新的二次开发技术动向,并获得Autodesk公司专业开发支持顾 ...

  10. 微软SQL Server认证最新信息(17年5月22日更新),感兴趣的进来看看哟

    之前一直有在关注微软认证的一些消息,由于最新的SQL Server认证加入了2016的相关内容,导致课程资料需要大部分更新,但是微软更新相对比较慢,并且经常改版,目前发现的最新的MCP Cert Pa ...

随机推荐

  1. 重大发现,AQS加锁机制竟然跟Synchronized有惊人的相似

    在并发多线程的情况下,为了保证数据安全性,一般我们会对数据进行加锁,通常使用Synchronized或者ReentrantLock同步锁.Synchronized是基于JVM实现,而Reentrant ...

  2. JUC学习笔记——共享模型之不可变

    JUC学习笔记--共享模型之不可变 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的不可变内容 我们会分为以下几部分进行介绍: 不可变案例 不可变设计 模式之享元 原理之final ...

  3. (C++) 类与 static_cast 与 dynamic_cast

    static_cast static_cast相当于C语言里面的强制转换,适用于: 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换.进行上行转换(把派生类的指针或引用转换成基类表示) ...

  4. MySQL库,表,数据的操作

    数据库的操作 1. 创建数据库 create database [if not exists] `数据库名` charset=字符编码(utf8mb4); 如果多次创建会报错 如果不指定字符编码,默认 ...

  5. python-面向过程与函数式

    面向过程与函数式 面向过程 "面向过程"核心是"过程"二字,"过程"指的是解决问题的步骤,即先干什么再干什么......,基于面向过程开发程 ...

  6. Blazor和Vue对比学习(进阶.路由导航一):基本使用

    Blazor和Vue都是单文件组件SPA,路由的实现逻辑非常相似,页面路径的改变都是组件的切换,但因为各自语言的特性,在实现方式上有较大差异. 一.安装 1.Vue:Router是Vue的一个插件.如 ...

  7. 【每日一题】【list转int数组】【Lambda的简化-方法引用】2022年1月15日-NC45 实现二叉树先序,中序和后序遍历

    描述 给定一棵二叉树,分别按照二叉树先序,中序和后序打印所有的节点.   数据范围:0 \le n \le 10000≤n≤1000,树上每个节点的val值满足 0 \le val \le 1000≤ ...

  8. 《MySQL必知必会》之快速入门游标和触发器

    第二十四章 使用游标 本章将介绍什么是游标以及如何使用游标 游标 之前的select语句检索出来的数据,没有办法得到第一行或者下一行 有时,需要在检索出来的行中前进或后退一行或多行.这就是使用游标的原 ...

  9. MongoDB从入门到实战之MongoDB简介

    前言 相信很多同学对MongoDB这个非关系型数据库都应该挺熟悉的,在一些高性能.动态扩缩容.高可用.海量数据存储.数据价值较低.高扩展的业务场景下MongoDB可能是我们的首选,因为MongoDB通 ...

  10. jmeter 之 JSON 断言

    1.JSON 断言所在位置:断言->JSON 断言 2.JSON断言中的字段解析 Assert JSON Path exists:json 表达式,判断所字段是否存在,存在则为True, 否则为 ...