Django自定义中间件
一. 什么是中间件
中间件是一个钩子框架,它们可以介入Django
的请求和响应处理过程。 它是一个轻量级、底层的插件系统,用于在全局修改Django 的输入或输出。
我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:
也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止,返回到网页上。
我们先来看一下Django基础中间件的源码:
django.utils.deprecation.py
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
要习惯于看源码,上面的这段代码并不复杂,下面我们来一一解释。
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
熟悉python
类的都不陌生__init__
方法, 这里主要是一次性配置和初始化
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
__call__
为每个请求/响应执行的代码
self.process_request(request)
为每个请求到调用视图之前的操作,通常可以在这里做一些用户请求频率的控制。
self.get_response(request)
为调用视图
self.process_response(request, response)
为调用视图完成后的操作。
1、中间件共分为
process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
2、使用方法
process_request(self, request) # 该方法在请求到来的时候调用。
process_view(self ,request, fnc , arg ,kwarg) # 在本次将要执行的View函数被调用前调用本函数。
process_response(self,request,response) # 在执行完View函数准备将响应发到客户端前被执行。
**process_exception(self,request, exception)** # View函数在抛出异常时该函数被调用,得到的exception参数是实际上抛出的异常实例。通过此方法可以进行很好的错误控制,提供友好的用户界面。
二. 自定义中间件
刚才了解了基础中间件,现在就开始编写我们自己的中间件。通常我们回去继承基础中间件来实现自己的功能
if django.VERSION[:3] >= (1, 10, 0):
# 新版本中,middle需要继承这个类
from django.utils.deprecation import MiddlewareMixin
else:
MiddlewareMixin = object
class PermissionMiddlewareMixin(MiddlewareMixin):
"""
django 中间件
"""
def process_request(self,request):
print '--------------request'
def process_view(self,request, view_func, *view_args, **view_kwargs):
print '--------------view'
def process_response(self,request, response): # 该方法必须返回 response
print '--------------response'
return response
三. 注册中间件
在setting.py
中
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',
'appname.middleware.middleware_name', # 在这里添加中间件的路径,就是注册中间件
]
四. 举个栗子?
1、将GET、POST参数整合到parameters
# -*- coding: utf-8 -*-
'''
请求初始化预处理:
1. GET、POST参数整合到parameters
'''
import django
from django.http import HttpResponseBadRequest, QueryDict
if django.VERSION[:3] >= (1, 10, 0):
# 新版本中,middle需要继承这个类
from django.utils.deprecation import MiddlewareMixin
BaseMiddleCls = MiddlewareMixin
else:
BaseMiddleCls = object
class RequestInitMiddleware(BaseMiddleCls):
'''请求初始化预处理'''
def process_request(self, request):
try:
# 1. GET参数整合到parameters
request.parameters = request.GET.copy()
if request.method == "POST":
# 2. 处理request.body内的query_string更新到request.parameters
# multipart/form-data 时不处理表单里的参数,因为没有经过nginx的参数签名校验,无法保证参数合法性
if request.META['CONTENT_TYPE'].startswith('multipart/form-data'):
return None
# application/x-www-form-urlencoded 时不处理request.body
elif request.META['CONTENT_TYPE'] == 'application/x-www-form-urlencoded':
pass
# xml or json 时不处理request.body
elif request.body.startswith("<") or request.body.startswith("{") or request.body.startswith("["):
pass
# 其他情况更新request.body内的query_string到request.parameters
elif '=' in request.body:
request.parameters.update(QueryDict(request.body, encoding='utf-8'))
# 3. 表单参数更新到request.parameters
for k in request.POST:
# 使用setlist以支持类似复选控件一个name多个value的情况
request.parameters.setlist(k, request.POST.getlist(k))
return None
except:
response = HttpResponseBadRequest()
return response
2、客户端请求频率限制
# coding:utf-8
from django.utils.deprecation import MiddlewareMixin
from django.http.response import HttpResponse
from django_redis import get_redis_connection
from hashlib import md5
class RequestBlockMiddlewareMixin(MiddlewareMixin):
"""
django中间件客户端请求频率限制
"""
limit = 4 # 单位时间内允许请求次数
expire = 1 # 限制时间
cache = "default" # 获取django cache
def process_request(self, request):
num = self.set_key(request)
if num > self.limit:
return HttpResponse("请求频率过快,请稍后重试", status=503)
@staticmethod
def get_ident(request):
"""
Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
if present and number of proxies is > 0. If not use all of
HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
"""
NUM_PROXIES = 1
xff = request.META.get('HTTP_X_FORWARDED_FOR')
remote_addr = request.META.get('REMOTE_ADDR')
num_proxies = NUM_PROXIES
if num_proxies is not None:
if num_proxies == 0 or xff is None:
return remote_addr
addrs = xff.split(',')
client_addr = addrs[-min(num_proxies, len(addrs))]
return client_addr.strip()
return ''.join(xff.split()) if xff else remote_addr
def get_md5(self, request):
"""
获取IP md5值
:param request:
:return:
"""
ip_str = self.get_ident(request)
ip_md5 = md5()
ip_md5.update(ip_str.encode("utf-8"))
return ip_md5.hexdigest()
def set_key(self, request):
"""
通过redis lua脚本设置请求请求次数和限制时间
:param request:
:return: 限制时间内请求次数
"""
lua = """
local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],ARGV[1])
end
return tonumber(redis.call("get", KEYS[1]))
"""
key = self.get_md5(request)
redis_cli = get_redis_connection(self.cache)
data = redis_cli.eval(lua, 1, key, self.expire, self.limit)
return data
相关链接:
http://lxpgj.com/article/60
https://my.oschina.net/esdn/blog/825194
https://code.ziqiangxuetang.com/django/django-middleware.html
https://carey.akhack.com/2018/11/20/django%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%A4%84%E7%90%86/
https://gitee.com/careyjike_173/codes/0v4fwk8ytdm62iue3aj9h28
上一篇:Python操作Redis