详解django自定义中间件处理

yipeiwu_com5年前Python基础

中间件是一个钩子框架,它们可以介入 Django 的请求和响应处理过程。 它是一个轻量级、底层的 插件 系统,用于在 全局修改 Django 的输入或输出 。

每个中间件组件负责完成某个特定的功能

这里介绍的中间件方法适用于 Django1.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() 方法一定要 return 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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持【听图阁-专注于Python设计】。

相关文章

Python按行读取文件的实现方法【小文件和大文件读取】

本文实例讲述了Python按行读取文件的实现方法。分享给大家供大家参考,具体如下: 小文件: #coding=utf-8 #author: walker #date: 2013-12...

django foreignkey外键使用的例子 相当于left join

django外键使用 一对一 因为django中处于安全和方便将数据库中的表封装成模型,所以很多sql原生的功能无法使用, 比如 left join,但是我们可以使用外键(foreign...

python中ASCII码字符与int之间的转换方法

ASCII码转换为int:ord('A') 65 int转为ASCII码:chr(65) 'A' 题目内容: 实现一个凯撒密码的变种算法,对输入字符串进行加解密处理 把字母a-z分别循环...

python urllib urlopen()对象方法/代理的补充说明

python urllib urlopen()对象方法/代理的补充说明 urllib 是 python 自带的一个抓取网页信息一个接口,他最主要的方法是 urlopen(),是基于 py...

python中关于日期时间处理的问答集锦

如何在安装setuptools模块时不生成egg压缩包而是源码     Q:如何在安装setuptools模块时不生成egg压缩包而是源码,这样有时可以修改...