Django-中间件
1.Django中间件及两个重要方法
1.django有7个中间件,并且还支持用户自定义中间件。
2.当我们完成一些全局相关的功能(例如用户访问频率)、权限(中间件会获取到用户的权限以及访问的路由,并且查看该用户是否有该路由的权限)时需要用到中间件。
3.Django的中间件在settings中的MIDDLEWARE内。在中间件中我们发现中间件其实是一个类,但是多个类中都有两个相同的方法:process_request和process_response。
4.如何自定义中间件:
4.1在应用或者django项目下自定义目录
4.2参考自带中间件的代码编写并继承
4.3在类中编写五个可以自定义的方法(允许个别没有):
需要掌握:
process_request
process_response
需要了解:
process_view
process_exception
process_template_response
5.中间件写好之后一定要在settings.py中的MIDDLEWARE中注册中间件才可以使用。
6.process_request:除了self还需一个参数request。请求来的时候会从上往下依次执行每一个注册了的中间件(类)中的process_request方法(函数)。没有该方法的中间件会依次跳过。MIDDLEWARE中中间件中排名越靠前,就限执行谁的该方法。
并且如果该方法返回了Httpresponse对象,那么就直接原路返回不再往后执行。
7.process_response:除了self还需两个参数request和response。请求走的时候会从下往上依次经过每一个中间件的该方法。response就是需要处理的数据。并且process_response方法还有返回值,返回值是response(也可以替换)。
8.如果在执行process_request方法的时候直接返回了HttpResponse对象,网络请求就不会再走下面的MyMiddleware2,而是直接走MyMiddleware1中的process_response,不再经过MyMiddleware2。
2.Django中间件三个了解的方法
1.process_view:路由能匹配成功之后,process_request方法执行之后,视图函数/类执行之前执行(顺序同request_request)。process_view需要4个形参:
def process_view(self,request,view_func,view_args,view_kwargs):
print('from Mymiddleware2 process_view')
"""
并且当我们打印几个参数发现,view_func,view_args,view_kwargs分别是函数名,函数的参数。
print(view_func,view_args,view_kwargs) # <function index at 0x000002358F8D43A0> () {}
"""
process_view的返回值为None的时候,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数,直接返回该HttpResponse对象
2.process_exception:当视图函数报错之后触发,有两个形参:request,exception。exception是报错信息。在执行完视图函数之后,执行process_response之前执行。
def process_exception(self,request,exception):
print(exception) # name 'aaaaa' is not defined
print('from Mymiddleware1 process_exception')
3.process_template_response
视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
3.基于Django中间件的功能设计
1.Django中间件的字符串并不是真的字符串,而是将各个功能制作成配置文件的字符串形式,如果想拥有该功能就编写对应的字符串,如果不用这个功能就注释掉相印的字符串。
2.如何解决字符串导入模块的问题?importlib模块可以字符串变成模块导入的形式:
from aaa import a
print(a) # <module 'aaa.a' from 'D:\\上海python金牌班\\20221222 day66 Django\\abc\\aaa\\a.py'>
s1 = 'aaa.a'
import importlib
res = importlib.import_module(s1)
print(res) # <module 'aaa.a' from 'D:\\上海python金牌班\\20221222 day66 Django\\abc\\aaa\\a.py'>
变量名a和res指代的都是一个模块名,并且它们的结果一样,说明importlib.import_module(变量名)可以将字符串转化为导入模块的形式。
"""
该模块实现的底层原理是:将字符串按照最右边的.分割,前一部分是路径,后一部分是模块文件名,所以也意味着该模块实现的最小单位(字符串的结尾)是模块文件名而不是文件里面的名字。
'aaa.bbb.b'>>>from aaa.bbb import c
"""
3.利用中间件原理设计功能:模拟用qq,email,微信发送同一句消息,并且同时发送。我们可以分别涉及几个函数,再用一个函数将它们封起来,传入参数,也可以实现。
第二种方法:参考中间件原理,利用字符串路径拿到模块名称,在利用反射,拿到指定的类,生成多个对象,分别点不同类之间的方法。
目录结构:
总目录:
dictory
settings.py
start.py
notify
_init_.py
email.py
qq.py
wechat.py
代码:
settings.py: # 用于路径拆分
notify = [
'notify.email.Email',
'notify.qq.Qq',
'notify.wechat.Wechat'
]
start.py: # 启动文件
import notify
if __name__ == '__main__':
notify.send_all('今天星期天')
_init_.py:
from dictory import settings
import importlib
def send_all(content):
for notify_str in settings.notify: # 'notify.email.Email'
# 1.先分割,拿到的是字符串的模块名,类名
module_path,class_str_name = notify_str.rsplit('.',maxsplit=1)
# 2.通过字符串路径拿到模块文件
module_name = importlib.import_module(module_path)
# 3.模块文件可以看做对象,利用反射拿到类名
class_name = getattr(module_name,class_str_name)
obj = class_name()
obj.send_msg('圣诞节的密码') # 多个类中有相同名称的功能,体现了多态性
email.py:
class Email(object):
def __init__(self):
pass # 模拟发送消息前的一些操作
def send_msg(self,content):
print('来自于Email的消息:%s' % content)
qq.py:
class Qq(object):
def __init__(self):
pass
def send_msg(self,content):
print('来自qq的消息:%s' % content)
wechat.py:
class Wechat(object):
def __init__(self):
pass
def send_msg(self,content):
print('来自wechat的消息:%s' % content)
4.csrf跨站请求伪造
1.钓鱼网站:模仿一个正规网站,让用户在该网站上进行操作,但操作的结果会影响到用户正常的网站账户,但是其中有一些猫腻。
eg:英语四六级考试需要网上提前缴费,但是会发现扣了钱但是却没有提交给真正的官方。这是因为我们向真网站发送请求时其实是发送到了假网站假网站,当我们点击付款时我们其实是向真网站发送了请求,但是假网站却悄悄改掉了转账的目标账户。我们转钱就转入了一个其他的账户。所以我们需要解决的问题就是:如何其他网站的请求方法送到正规网站。
代码模拟:
真网站:
views.py:
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_name = request.POST.get('target_user')
money = request.POST.get('money')
print(f'用户{username}给{target_name}转账了{money}')
return render(request,'transfer.html')
transfer.html:
<body>
<h2>我是真的网站</h2>
<form action="" method="post">
<p>用户名:
<input type="text" name="username">
</p>
<p>转账目标账户
<input type="text" name="target_user">
</p>
<p>转账金额:
<input type="text" name="money">
</p>
<input type="submit" value="提交">
</form>
</body>
假网站:
views.py:
def transfer(request):
return render(request,'transfer.html')
transfer.html:
<body>
<h2>我是假的网站</h2>
<form action="http://127.0.0.1:8000/transfer/" method="post">
{% csrf_token %}
<p>用户名:
<input type="text" name="username">
</p>
<p>转账目标账户
<input type="text" >
<input type="text" name="target_user" value="黑客jason">
</p>
<p>转账金额:
<input type="text" name="money">
</p>
<input type="submit" value="提交">
</form>
</body>
5.csrf校验策略
1.网站区分请求页面是真还是假的途径,是给真的提交数据的页面添加一个唯一标识。
2.如何在form表单中添加csrf唯一标识?
在html页面上的form表单内部添加{% csrf_token %}。这样settings校验csrf的那句中间件就可以不用再注掉,凭借中间件就可以想页面发送请求。
即使在假的页面上添加了唯一标识,但是那是假页面的唯一标识,无法想真的页面发送请求。
3.如何给ajax请求添加唯一标识?
方式1:给data中增添一个键值对(一定要用引号引起来):
data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'},
方式2:引入js脚本:
在django项目中新建静态文件static,在settings.py中加入以下代码:
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
在static中新建一个js文件,在js文件中加入以下代码:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
在加入ajax中的script标签中加入:src="/static/myjs.js"
6.csrf相关装饰器
1.当整个django项目都校验csrf,但是某些视图函数(类)不想校验。或者是整个django项目都不校验csrf,但是某些视图函数(类)想校验。这种效果我们可以通过装饰器实现。
2.FBV添加装饰器:导入模块,直接将相关装饰器添加在相关视图函数上面即可
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt:整个django项目要校验csrf,被装饰的函数不校验
@csrf_protect:整个django项目不校验csrf,被装饰的函数要校验
3.CBV添加装饰器:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
方式1:导入模块,在相应函数上面添加@method_decorator(csrf_protect),全局不校验,指定函数校验。
class Myclass(views.View):
def get(self, request):
return render(request, 'CBV.html')
@method_decorator(csrf_exempt)
def post(self, request):
return HttpResponse('哈哈哈')
方式2:导入模块,在类上面添加该装饰器,用name指定需要添加的函数名:
@method_decorator(csrf_protect, name='post')
class Myclass(views.View):
def get(self, request):
return render(request, 'CBV.html')
def post(self, request):
return HttpResponse('哈哈哈')
方式3:导入模块,在类中dispatch方法上添加装饰器,该方法对类中所有方法都有效:
class Myclass(views.View):
@method_decorator(csrf_protect)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self, request):
return render(request, 'CBV.html')
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律