Django中间件与上下文渲染器

django中一个请求的流程

我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:


Django中间件与上下文渲染器_第1张图片
image.png

也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止,返回到网页上。

Django中间件(Middleware)

  • 中间件,顾名思义,就是处在中间的一些软件。比如匹配到了URL,但是还没有执行view函数的时候,这个时候可以执行一些代码,这个代码就是中间件。
  • Django已经内置了许多中间件,这些中间件已经可以满足90%以上的需求,如果还不够,还可以定义自己的中间件,以下将对Django自带的中间件进行详细解释,自带在settings.py中MIDDLEWARE配置:


    Django中间件与上下文渲染器_第2张图片
    image.png
  1. django.middleware.security.SecurityMiddleware:一些安全设置,比如XSS脚本过滤。
  2. django.contrib.sessions.middleware.SessionMiddleware:session支持中间件,加入这个中间件,会在数据库中生成一个django_session的表。
  3. django.middleware.common.CommonMiddleware:通用中间件,会处理一些URL,比如baidu.com会自动的处理成www.baidu.com。比如/blog/111会处理成/blog/111/自动加上反斜杠。
  4. django.middleware.csrf.CsrfViewMiddleware:跨域请求伪造中间件。加入这个中间件,在提交表单的时候会必须加入csrf_token,cookie中也会生成一个名叫csrftoken的值,也会在header中加入一个HTTP_X_CSRFTOKEN的值来放置CSRF攻击。
  5. django.contrib.auth.middleware.AuthenticationMiddleware:用户授权中间件。他会在每个HttpRequest对象到达view之前添加当前登录用户的user属性,也就是你可以在view中通过request访问user。
  6. django.contrib.messages.middleware.MessageMiddleware:消息中间件。展示一些后台信息给前端页面。如果需要用到消息,还需要在INSTALLED_APPS中添加django.contrib.message才能有效。如果不需要,可以把这两个都删除。
  7. django.middleware.clickjacking.XFrameOptionsMiddleware:防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现。

中间件执行顺序

  • 在调用视图前,按照settings中配置的MIDDLEWARE顺序执行
  • 在调用视图后,中间件会按相反的顺序执行

编写一个自定义的中间件

  • 你可以定义一个独立的python类来作为一个自定义中间件
  1. __init__(self, get_response):
    中间件的初始化只会在django启动时调用一次,并且只接受get_response函数,所以一般都不会去重写它
    def __init__(self,get_response):
        #get_response其实就是我们的view function
        self.get_response = get_response

2.__call__(self, resquest):

def __call__(self, request):
    # 在这里编写执行view视图需要处理的代码    
    response = self.get_response(request)
  # 在这里编写执行view视图后需要处理的代码
    return response

ps: response = self.get_response(request) 必须要有
settings.py中添加自定义的中间件:

Django中间件与上下文渲染器_第3张图片
添加

# -*- coding: utf-8 -*-
from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
#原生中间件
class MiddleWareTest:
    def __init__(self,get_response):
        #get_response其实就是我们的view function
        self.get_response = get_response
        self.count = {}
    def __call__(self,request):
        print('view执行之前')
        # 在这里编写执行view视图需要处理的代码
        # 防止访问频率过高
        #有些网站服务器会使用ngix等代理http,或者是该网站做了负载均衡,导致使用remote_addr抓取到的是1270
        #这时使用HTTP_X_FORWARDED_FOR才获得是用户的真实IP。推荐使用以下代码:
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META['HTTP_X_FORWARDED_FOR']
            print(ip)
        else:
            #使用django来获取用户访问的IP地址,如果用户是正常情况下
            ip = request.META['REMOTE_ADDR']

        count = self.count.get(ip,0) #不存在则返回自定的0,存在则返回ip对应的值;

        if count and count > 3:
            #其实就是返回403拒绝访问
            return HttpResponseForbidden
        else:
            count += 1
            self.count[ip] = count
            #这段代码其实就是执行我们的view视图
            response = self.get_response(request)

            # 这里编写你需要的请求之后的处理
            print('view执行之后')
            return response
#中间件除了类的方法,还可以用function
  • 在调用视图前,他有以下函数
    process_view(request, view_func, view_args, view_kwargs): return None or HttpResponse
  • 调用视图之后
    process_template_response(request, response): return 实现了render方法的HttpResponse
    如果需要调用这个函数,返回的response必须包含render方法,一般该函数做模版内容处理
    process_exception(request, exception): return None or HttpResponse
    该请求在报出异常时,执行该方法
from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponseServerError
from django.utils.deprecation import MiddlewareMixin
class MiddleWareTest(MiddlewareMixin):
    def process_view(self, request, view_func, view_args, view_kwargs):
        """
            @request, 当前请求对象
            @view_func, 就是我们需要执行的view_func方法,
            @view_args, 就是我们的view_func所带的位置参数
            @view_kwargs, 就是我们的view_func所带的键值参数
        """
        print('process_view')
        response = view_func(request,*view_args,**view_kwargs)
        # 这个方法如果返回的是一个response, 它就提前结束请求
        return response

    def process_template_response(request, response):

        print('process_template_response')
        return response

    def process_exception(self, request, exception):
        # 这个异常必须是在执行view中出现的异常,它才能进入这个里面

        print('----exception----------')

中间件开发准则

  • 不能继承自任何类(可以继承 object )
  • 中间件可以存在于你Python 路径中的任何位置。 Django所关心的只是被包含在MIDDLEWARE中的配置。
  • 将Django 中可用的中间件作为例子随便看看。https://docs.djangoproject.com/en/1.11/ref/middleware/
  • 如果你认为你写的中间件组建可能会对其他人有用,那就把它共享到社区! 让我们知道它,我们会考虑把它添加到Django中
  • 中间件一般是实现一个系统级别的应用或者面向整个工程应用,所以一般情况下,中间件不能有业务代码

使用中间件开发流量统计

  1. 添加一个middleware类
    你创建的类原则上是可以放置在任何路径,只要你能够引用,如果你要写自己的中间件,我们一般会在某个app下添加一个middleware文件或者创建一个文件夹,专门存储中间件文件

'''
流量统计
'''
class StatFlowMiddleware(object):
    def __init__(self,get_response):
        self.get_response = get_response
        self.filename = 'count.txt'
    def __call__(self, request):
        # 获取IP
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META['HTTP_X_FORWARDED_FOR']
        else:
            ip = request.META['REMOTE_ADDR']
        with open(self.filename,'a+') as f:
            f.seek(0)
            content = f.read()
        if content:
            content = json.loads(content) #序列化,将json格式转换为python对象
            count = content.get(ip)
            if count > 5:
                return HttpResponseForbidden()
            # 这段代码其实就是执行我们的view视图
            response = self.get_response(request)
            # 只有这个请求返回为200,才代表这个请求需要被统计
            if response.status_code == 200:
                if content:
                    content[ip] = content[ip] + 1
                else:
                    content = {ip: 1}
                with open(self.filename, "w+") as f:
                    f.write(json.dumps(content)) #反序列化,python对象转化为json格式
            return response
  1. 添加至settings.py
    Django中间件与上下文渲染器_第4张图片
    image.png

Django上下文渲染器

有时候我们想让一些内容在多个模板中都要有,比如导航内容,我们又不想每个视图函数都写一次这些变量内容,怎么办呢?这时候就可以用 Django 上下文渲染器来解决。

  • 内置的上下文渲染器
    Django中间件与上下文渲染器_第5张图片
    image.png
django.template.context_processors.debug: 测试模块
debug - True。你可以在模板中用它测试是否在DEBUG 模式。
sql_queries –  一个{'sql': ..., 'time': ...} 字典的列表,表示请求期间到目前为止发生的每个SQL 查询及花费的时间。这个列表按查询的顺序排序,并直到访问时才生成。
django.template.context_processors.request: 处理请求模块
requset - 表示当前的HttpRequest。
django.contrib.auth.context_processors.auth: django内置的用户登录管理
user – 一个 auth.User实例代表当前登录的用户 (或者 一个 AnonymousUser 实例, 如果用户没有登录).
perms – 一个 django.contrib.auth.context_processors.PermWrapper实例, 代表当前登录用户所拥有的权限.
django.contrib.messages.context_processors.messages:
messages – 通过消息框架设置的消息(字符串形式)列表。
DEFAULT_MESSAGE_LEVELS – 消息等级名称到它们数值 的映射。
学以致用
  1. 使用中间件完成数据统计,每个IP只统计一次,看看每分钟有多少个IP访问当前网站。
  • 开发中间件middleware.py
import datetime
'''
使用中间件完成数据统计,每个IP只统计一次,看看每分钟有多少个IP访问当前网站;
1.获取访问ip,然后写入到文件里面去;
如果文件里面存在,那么不写入;
'''
class StatisticsIpware:
    startTime = datetime.datetime.now()
    file = 'count.txt'
    count = 0
    def __init__(self,get_response):
        self.get_response = get_response
    def __call__(self, request):
        #获取ip
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META['HTTP_X_FORWARDED_FOR']
        else:
            ip = request.META['REMOTE_ADDR']
        with open(StatisticsIpware.file,'a+') as f: #a+ 若文件存在,则文件指针将位于结尾,若该文件不存在,创建新文件用于读写。
            f.seek(0)  #将指针移动到开头,以便read从开头到末尾读取
            content = f.read()
        # 这段代码其实就是执行我们的view视图
        response = self.get_response(request)
        #执行view后执行
        self.visiTime = datetime.datetime.now()
        spaceTime = (self.visiTime - StatisticsIpware.startTime).seconds #利用求出时间差,seconds获取秒
        if response.status_code == 200:
            if spaceTime <= 30: #如果时间差小于1分钟
                print(ip)
                if ip not in content: #如果不存在ip
                    with open(StatisticsIpware.file,'a+') as f:
                        f.write('{},'.format(ip)) #则写入
                        StatisticsIpware.count += 1
            else:
                print('当前访问量1分钟{}'.format(StatisticsIpware.count))
        return response
  • settings.py配置中间件
    Django中间件与上下文渲染器_第6张图片
    image.png
  1. 使用上下文渲染器完成首页菜单栏
  • 定义上下文渲染器context_processors.py
# -*- coding: utf-8 -*-
def menu_bar(request):
    return {'first':u'首页','second':u'基础教程','third':u'项目实战','fourth':u'每日一练'}

def my_ip(request):
    # 获取ip
    if 'HTTP_X_FORWARDED_FOR'  in request.META:
        ip = request.META['HTTP_X_FORWARDED_FOR']
    else:
        ip = request.META['REMOTE_ADDR']
    return {'IP':ip}
  • 上下文渲染器 加入到settings.py 中:
    Django中间件与上下文渲染器_第7张图片
    image.png
  • 页面模板:
    基础模板menu_base.html



    
    {% block title %}菜单模板{% endblock %}
    



访问ip:{% block ip%}{% endblock %}

首页indexpage.html

{% extends "menu_base.html" %}
{% block title %}首页{% endblock %}
{% block li_first%}{{first}}{% endblock %}
{% block li_second%}{{second}}{% endblock %}
{% block li_third%}{{third}}{% endblock %}
{% block li_fourth%}{{fourth}}{% endblock %}
{% block ip %}{{IP}}{% endblock %}

主页·homepage.html

{% extends "menu_base.html" %}
{% block title %}主页{% endblock %}
{% block li_first%}{{first}}{% endblock %}
{% block li_second%}{{second}}{% endblock %}
{% block li_third%}{{third}}{% endblock %}
{% block li_fourth%}{{fourth}}{% endblock %}
{% block ip %}{{IP}}{% endblock %}

效果:
Django中间件与上下文渲染器_第8张图片
image.png

Django中间件与上下文渲染器_第9张图片
image.png

你可能感兴趣的:(Django中间件与上下文渲染器)