django中一个请求的流程
我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下:
也就是说,每一个请求都是先通过中间件中的 process_request 函数,这个函数返回 None 或者 HttpResponse 对象,如果返回前者,继续处理其它中间件,如果返回一个 HttpResponse,就处理中止,返回到网页上。
Django中间件(Middleware)
- 中间件,顾名思义,就是处在中间的一些软件。比如匹配到了URL,但是还没有执行view函数的时候,这个时候可以执行一些代码,这个代码就是中间件。
-
Django已经内置了许多中间件,这些中间件已经可以满足90%以上的需求,如果还不够,还可以定义自己的中间件,以下将对Django自带的中间件进行详细解释,自带在settings.py中MIDDLEWARE配置:
- django.middleware.security.SecurityMiddleware:一些安全设置,比如XSS脚本过滤。
- django.contrib.sessions.middleware.SessionMiddleware:session支持中间件,加入这个中间件,会在数据库中生成一个django_session的表。
- django.middleware.common.CommonMiddleware:通用中间件,会处理一些URL,比如baidu.com会自动的处理成www.baidu.com。比如/blog/111会处理成/blog/111/自动加上反斜杠。
- django.middleware.csrf.CsrfViewMiddleware:跨域请求伪造中间件。加入这个中间件,在提交表单的时候会必须加入csrf_token,cookie中也会生成一个名叫csrftoken的值,也会在header中加入一个HTTP_X_CSRFTOKEN的值来放置CSRF攻击。
- django.contrib.auth.middleware.AuthenticationMiddleware:用户授权中间件。他会在每个HttpRequest对象到达view之前添加当前登录用户的user属性,也就是你可以在view中通过request访问user。
- django.contrib.messages.middleware.MessageMiddleware:消息中间件。展示一些后台信息给前端页面。如果需要用到消息,还需要在INSTALLED_APPS中添加django.contrib.message才能有效。如果不需要,可以把这两个都删除。
- django.middleware.clickjacking.XFrameOptionsMiddleware:防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现。
中间件执行顺序
- 在调用视图前,按照settings中配置的MIDDLEWARE顺序执行
- 在调用视图后,中间件会按相反的顺序执行
编写一个自定义的中间件
- 你可以定义一个独立的python类来作为一个自定义中间件
- __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中添加自定义的中间件:
# -*- 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中
- 中间件一般是实现一个系统级别的应用或者面向整个工程应用,所以一般情况下,中间件不能有业务代码
使用中间件开发流量统计
- 添加一个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
- 添加至
settings.py
:
Django上下文渲染器
有时候我们想让一些内容在多个模板中都要有,比如导航内容,我们又不想每个视图函数都写一次这些变量内容,怎么办呢?这时候就可以用 Django 上下文渲染器来解决。
- 内置的上下文渲染器
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 – 消息等级名称到它们数值 的映射。
学以致用
- 使用中间件完成数据统计,每个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配置中间件
- 使用上下文渲染器完成首页菜单栏
- 定义上下文渲染器
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
中:
- 页面模板:
基础模板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 %}
效果: