中间件 Middleware

  • 中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
  • 每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。
  • 中间件类须继承自 django.utils.deprecation.MiddlewareMixin
  • 中间件类须实现下列五个方法中的一个或多个:
  • def process_request(self, request): 执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
  • def process_view(self, request, callback, callback_args, callback_kwargs): 调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
  • def process_response(self, request, response): 所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象
  • def process_exception(self, request, exception): 当处理过程中抛出异常时调用,返回一个HttpResponse对象
  • def process_template_response(self, request, response): 在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象
  • 注: 中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponese对象时表示此请求结束,直接返回给客户端
  • 中间件request和view的执行顺序根据在setting.py文件中注册的顺序从上到下执行,response的执行顺序是从下到上执行
# file : middleware/mymiddleware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class MyMiddleWare(MiddlewareMixin):
    def process_request(self, request):
        print("中间件方法 process_request 被调用")

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("中间件方法 process_view 被调用")

    def process_response(self, request, response):
        print("中间件方法 process_response 被调用")
        return response
  • 注册中间件
# file : settings.py
MIDDLEWARE = [
    ...
    # 导入自定义中间件 包名.模块名.中间件类
    'mymw.mymiddleware.MyMiddleware',
       ]
  • 中间件的执行过程

middleware.jpeg

from django.utils.deprecation import MiddlewareMixin

class MyMiddleware(MiddlewareMixin):
    def process_request(self, request):
        '''
        当请求经过处理进入到django程序中时 会先按照中间件的注册顺序 依次执行所有中间件的process_request方法
        :param request: 经过处理的当前的请求对象 HttpRequest 所有中间件共用一个请求对象
        :return: None  表示当前中间件中的功能执行完成 按照正常的流程继续向下执行代码
                 HttpResponse对象 表示当前流程执行完毕 进入django的响应流程 不再继续执行后面中间件的process_request方法 直接执行当前中间件的process_response方法
        '''
        print('自定义的第一个中间件中的process_request方法被调用')
        # return None

    def process_view(self, request, callback, callback_args, callback_kwargs):
        '''
        在django程序路由匹配之后 视图函数执行之前 会按照中间件的注册顺序 依次执行所有中间件的process_view方法
        :param request: HttpRequest对象 所有中间件共用request对象
        :param callback: 视图函数
        :param callback_args: 视图函数的位置参数
        :param callback_kwargs: 视图函数的关键字参数
        :return: None 表示程序可以继续向下执行 执行后面中间件的process_view或者执行视图函数
                 HttpResponse对象 不再执行后面中间件的process_view或者执行视图函数 进入django的响应流程 立即执行最后的中间件的process_response方法
        '''
        print('自定义的第一个中间件的process_view执行成功')

    def process_exception(self, request, exception):
        '''
        当视图函数执行过程中发生异常时 会按照中间件的注册顺序倒序执行所有中间件的process_exception 如果所有的中间件中都没有对异常进行处理 会执行django默认的异常处理程序 然后将响应结果交给最后一个中间件的process_response倒序执行
        :param request: 请求对象
        :param exception: 异常对象
        :return: None 将异常交给上一个中间件的process_exception进行处理
                 HttpResponse 不再执行其他中间件的process_exception 直接进入响应流程
        '''
        print('第一个中间件的process_exception执行成功')

    def process_response(self, request, response):
        '''
        当视图函数执行完成、或者其他中间件返回HttpResponse对象时 会按照中间件的注册顺序倒序执行所有中间件的process_response
        :param request: HttpRequest对象
        :param response: HttpResponse对象
        :return: 必须是HttpResponse对象
                 如果是参数中的 response 表示不对响应做任何处理
                 如果是重新创建的 HttpResponse对象 表示重写之前的响应
        '''
        print('自定义的第一个中间件的process_response对象执行成功')
        return response
        # return HttpResponse('这是第一个中间件重写后的响应')

跨站请求伪造攻击 CSRF

  • 某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)
  1. settings.py中确认 MIDDLEWARE 中django.middleware.csrf.CsrfViewMiddleware是否打开
  2. 模板中,form标签下添加如下标签
{% csrf_token %}
  • 如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

分页

  • 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据
  1. 方便阅读
  2. 减少数据提取量,减轻服务器压力
  • Django提供了Paginator类可以方便的实现分页功能
  • Paginator类位于django.core.paginator 模块中。

Paginator对象

负责分页数据整体的管理

  • 对象的构造方法
  • paginator = Paginator(object_list, per_page)

参数

  • object_list 需要分类数据的对象列表
  • per_page 每页数据个数

返回值

  • Paginator的对象

Paginator属性

  • count:需要分类数据的对象总数

  • num_pages:分页后的页面总数

  • page_range:从1开始的range对象, 用于记录当前面码数

  • per_page 每页数据的个数

  • Paginator方法

page(number)

  • 参数 number为页码信息(从1开始)
  • 返回当前number页对应的页信息
  • 如果提供的页码不存在,抛出InvalidPage异常

Paginator异常exception

  • InvalidPage:总的异常基类,包含以下两个异常子类
  • PageNotAnInteger:当向page()传入一个不是整数的值时抛出
  • EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出

Page对象

  • 负责具体某一页的数据的管理
#Paginator对象的page()方法返回Page对象
page = paginator.page(页码)

Page对象属性

  • object_list:当前页上所有数据对象的列表
  • number:当前页的序号,从1开始
  • paginator:当前page对象相关的Paginator对象

Page对象方法

  • has_next():如果有下一页返回True
  • has_previous():如果有上一页返回True
  • has_other_pages():如果有上一页或下一页返回True
  • next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
  • previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
  • len():返回当前页面对象的个数

说明

分页示例

from django.core.paginator import Paginator
def book(request):  
    bks = Book.objects.all()
    paginator = Paginator(bks, 10)
    cur_page = request.GET.get('page', 1)  # 得到默认的当前页
    page = paginator.page(cur_page)
    return render(request, 'bookstore/book.html', locals())
  • 模板设计
<html>
<head>
    <title>分页显示</title>
</head>
<body>
    {% for b in page %}
        <div>{{ b.title }}</div>
    {% endfor %}

        {% if page.has_previous %}
            <a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页</a>
        {% else %}
            上一页
        {% endif %}

    {% for p in paginator.page_range %}
        {% if p == page.number %}
            {{ p }}
        {% else %}
            <a href="{% url 'book' %}?page={{ p }}">{{ p }}</a>
        {% endif %}
    {% endfor %}

    {% if page.has_next %}
        <a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页</a>
    {% else %}
        下一页
    {% endif %}
</body>
</html>