经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Django » 查看文章
详解django自定义中间件处理
来源:jb51  时间:2018/11/22 10:00:37  对本文有异议

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

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

这里介绍的中间件方法适用于 Django1.10 以上

相关文件: django middleware

Django基础中间件

  1. django.utils.deprecation.py
  2.  
  3. class MiddlewareMixin(object):
  4. def __init__(self, get_response=None):
  5. self.get_response = get_response
  6. super(MiddlewareMixin, self).__init__()
  7.  
  8. def __call__(self, request):
  9. response = None
  10. if hasattr(self, 'process_request'):
  11. response = self.process_request(request)
  12. if not response:
  13. response = self.get_response(request)
  14. if hasattr(self, 'process_response'):
  15. response = self.process_response(request, response)
  16. return response

以上为Django基础中间件源码,要习惯于看源码,上面的这段代码并不复杂,下面我们来一一解释。

  1. def __init__(self, get_response=None):
  2. self.get_response = get_response
  3. super(MiddlewareMixin, self).__init__()

熟悉 python 类的都不陌生 __init__ 方法, 这里主要是 一次性配置和初始化

  1. def __call__(self, request):
  2. response = None
  3. if hasattr(self, 'process_request'):
  4. response = self.process_request(request)
  5. if not response:
  6. response = self.get_response(request)
  7. if hasattr(self, 'process_response'):
  8. response = self.process_response(request, response)
  9. return response

__call__ 为每个请求/响应执行的代码

self.process_request(request) 为每个请求到调用视图之前的操作,通常可以在这里做一些用户请求频率的控制。

self.get_response(request) 为调用视图

self.process_response(request, response) 为调用视图完成后的操作

自定义中间件

刚才了解了基础中间件,现在就开始编写我们自己的中间件。

通常我们回去继承基础中间件来实现自己的功能

  1. from django.utils.deprecation import MiddlewareMixin
  2.  
  3. class PermissionMiddlewareMixin(MiddlewareMixin):
  4. """
  5. django 中间件
  6. """
  7.  
  8. def process_request(self, request):
  9. pass
  10.  
  11. def process_response(self, request, response):
  12. return response
  13.  

如果你要在请求之前做处理,需要定义 process_request() 方法,去实现相关功能

如果你要在视图调用之后做处理,需要定义 process_response() 方法,去实现相关功能

:warning:注意 定义 process_response() 方法一定要 return response

需要将你编写的中间件添加到 settings 中的 MIDDLEWARE 里

我这里写了一个通过中间件限制客户端请求频率,有兴趣的可以看一下

django中间件客户端请求频率限制

通过redis lua脚本对客户端IP请求频率限制

  1. # coding:utf-8
  2.  
  3. __author__ = 'carey@akhack.com'
  4.  
  5. from django.utils.deprecation import MiddlewareMixin
  6. from django.http.response import HttpResponse
  7. from django_redis import get_redis_connection
  8. from hashlib import md5
  9.  
  10.  
  11. class RequestBlockMiddlewareMixin(MiddlewareMixin):
  12. """
  13. django中间件客户端请求频率限制
  14. """
  15.  
  16. limit = 4 # 单位时间内允许请求次数
  17. expire = 1 # 限制时间
  18. cache = "default" # 获取django cache
  19.  
  20. def process_request(self, request):
  21. num = self.set_key(request)
  22. if num > self.limit:
  23. return HttpResponse("请求频率过快,请稍后重试", status=503)
  24.  
  25. @staticmethod
  26. def get_ident(request):
  27. """
  28. Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
  29. if present and number of proxies is > 0. If not use all of
  30. HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
  31. """
  32. NUM_PROXIES = 1
  33. xff = request.META.get('HTTP_X_FORWARDED_FOR')
  34. remote_addr = request.META.get('REMOTE_ADDR')
  35. num_proxies = NUM_PROXIES
  36.  
  37. if num_proxies is not None:
  38. if num_proxies == 0 or xff is None:
  39. return remote_addr
  40. addrs = xff.split(',')
  41. client_addr = addrs[-min(num_proxies, len(addrs))]
  42. return client_addr.strip()
  43.  
  44. return ''.join(xff.split()) if xff else remote_addr
  45.  
  46. def get_md5(self, request):
  47. """
  48. 获取IP md5值
  49. :param request:
  50. :return:
  51. """
  52. ip_str = self.get_ident(request)
  53. ip_md5 = md5()
  54. ip_md5.update(ip_str.encode("utf-8"))
  55. return ip_md5.hexdigest()
  56.  
  57. def set_key(self, request):
  58. """
  59. 通过redis lua脚本设置请求请求次数和限制时间
  60. :param request:
  61. :return: 限制时间内请求次数
  62. """
  63. lua = """
  64. local current
  65. current = redis.call("incr",KEYS[1])
  66. if tonumber(current) == 1 then
  67. redis.call("expire",KEYS[1],ARGV[1])
  68. end
  69. return tonumber(redis.call("get", KEYS[1]))
  70. """
  71. key = self.get_md5(request)
  72. redis_cli = get_redis_connection(self.cache)
  73. data = redis_cli.eval(lua, 1, key, self.expire, self.limit)
  74. return data
  75.  

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号