Django自定义中间件开发指南:从原理到频率限制实战
本文详解Django中间件的工作原理与生命周期,解析MiddlewareMixin源码及__call__方法流程。指导开发者如何继承基础中间件实现process_request和process_response逻辑,并重点演示结合Redis Lua脚本构建客户端IP请求频率限制中间件的完整代码与配置方法。
中间件充当一个钩子框架,介入Django处理请求与响应的全过程。作为一种轻量级的底层插件系统,它允许在全局层面修改Django的输入或输出数据。
每个中间件组件专注于完成特定的单一功能。
本文介绍的中间件方法兼容Django 1.10及以上版本。
相关参考文件:django middleware
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
上述代码展示了Django基础中间件的源码。建议开发者养成阅读源码的习惯,该段代码逻辑并不复杂,接下来我们将逐一进行解释。
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) 为调用视图完成后的操作
自定义中间件开发
在了解基础中间件机制后,现在开始编写我们自己的中间件。
通常我们需要继承基础中间件来实现自定义功能。
from django.utils.deprecation import MiddlewareMixin
class PermissionMiddlewareMixin(MiddlewareMixin):
"""
django 中间件
"""
def process_request(self, request):
pass
def process_response(self, request, response):
return response
如果需要在请求到达视图之前进行处理,需定义process_request()方法并实现相关逻辑。
如果需要在视图调用之后进行处理,需定义process_response()方法并实现相关逻辑。
:warning:注意 定义process_response()方法时,务必返回response对象。
需要将编写好的中间件添加到settings配置文件的MIDDLEWARE列表中。
此处提供一个通过中间件限制客户端请求频率的示例,有兴趣的读者可以参考。
django中间件客户端请求频率限制
通过redis lua脚本对客户端IP请求频率限制
# coding:utf-8
__author__ = 'carey@akhack.com'
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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













