• 中间件的应用
    • Django中间件概述
    • 自定义中间件

    中间件的应用

    我们继续来完善投票应用。在上一个章节中,我们在用户登录成功后通过session保留了用户信息,接下来我们可以应用做一些调整,要求在为老师投票时必须要先登录,登录过的用户可以投票,否则就将用户引导到登录页面,为此我们可以这样修改视图函数。

    1. def praise_or_criticize(request: HttpRequest):
    2. """投票"""
    3. if 'username' in request.session:
    4. try:
    5. tno = int(request.GET.get('tno', '0'))
    6. teacher = Teacher.objects.get(no=tno)
    7. if request.path.startswith('/praise'):
    8. teacher.good_count += 1
    9. else:
    10. teacher.bad_count += 1
    11. teacher.save()
    12. data = {'code': 200, 'message': '操作成功'}
    13. except (ValueError, Teacher.DoesNotExist):
    14. data = {'code': 404, 'message': '操作失败'}
    15. else:
    16. data = {'code': 401, 'message': '请先登录'}
    17. return JsonResponse(data)

    前端页面在收到{'code': 401, 'message': '请先登录'}后,可以将用户引导到登录页面,修改后的teacher.html页面的JavaScript代码部门如下所示。

    1. <script>
    2. $(() => {
    3. $('.comment > a').on('click', (evt) => {
    4. evt.preventDefault()
    5. let a = $(evt.target)
    6. $.getJSON(a.attr('href'), (json) => {
    7. if (json.code == 200) {
    8. let span = a.next()
    9. span.text(parseInt(span.text()) + 1)
    10. } else if (json.code == 401) {
    11. location.href = '/login/?backurl=' + location.href
    12. } else {
    13. alert(json.message)
    14. }
    15. })
    16. })
    17. })
    18. </script>

    注意:为了在登录成功之后能够回到刚才投票的页面,我们在跳转登录时设置了一个backurl参数,把当前浏览器中的URL作为返回的页面地址。

    这样我们已经实现了用户必须登录才能投票的限制,但是一个新的问题来了。如果我们的应用中有很多功能都需要用户先登录才能执行,那么我们是不是需要在每个视图函数中添加代码来检查session中是否包含了登录用户的信息呢?答案是否定的,如果这样做了,我们的视图函数中必然会充斥着大量的重复代码。编程大师Martin Fowler曾经说过:代码有很多种坏味道,重复是最坏的一种。我们可以把验证用户是否登录这样的代码放到Django的中间件中。

    Django中间件概述

    中间件是安插在Web应用请求和响应过程之间的组件,它在整个Web应用中扮演了拦截过滤器的角色,通过中间件可以拦截请求和响应,并对请求和响应进行过滤(简单的说就是执行额外的处理)。通常,一个中间件组件只专注于完成一件特定的事,例如:Django框架通过SessionMiddleware中间件实现了对session的支持,又通过AuthenticationMiddleware中间件实现了基于session的请求认证。通过把多个中间件组合在一起,我们可以完成更为复杂的任务,Django框架就是这么做的。

    Django项目的配置文件中就包含了对中间件的配置,代码如下所示。

    1. MIDDLEWARE = [
    2. 'django.middleware.security.SecurityMiddleware',
    3. 'django.contrib.sessions.middleware.SessionMiddleware',
    4. 'django.middleware.common.CommonMiddleware',
    5. 'django.middleware.csrf.CsrfViewMiddleware',
    6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
    7. 'django.contrib.messages.middleware.MessageMiddleware',
    8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    9. ]

    我们稍微为大家解释一下这些中间件的作用:

    1. CommonMiddleware - 基础设置中间件,可以处理以下一些配置参数。
      • DISALLOWED_USER_AGENTS - 不被允许的用户代理(浏览器)
      • APPEND_SLASH - 是否追加/
      • USE_ETAG - 浏览器缓存相关
    2. SecurityMiddleware - 安全相关中间件,可以处理和安全相关的配置项。
      • SECURE_HSTS_SECONDS - 强制使用HTTPS的时间
      • SECURE_HSTS_INCLUDE_SUBDOMAINS - HTTPS是否覆盖子域名
      • SECURE_CONTENT_TYPE_NOSNIFF - 是否允许浏览器推断内容类型
      • SECURE_BROWSER_XSS_FILTER - 是否启用跨站脚本攻击过滤器
      • SECURE_SSL_REDIRECT - 是否重定向到HTTPS连接
      • SECURE_REDIRECT_EXEMPT - 免除重定向到HTTPS
    3. SessionMiddleware - 会话中间件。
    4. CsrfViewMiddleware - 通过生成令牌,防范跨请求份伪的造中间件。
    5. XFrameOptionsMiddleware - 通过设置请求头参数,防范点击劫持攻击的中间件。

    在请求的过程中,上面的中间件会按照书写的顺序从上到下执行,然后是URL解析,最后请求才会来到视图函数;在响应的过程中,上面的中间件会按照书写的顺序从下到上执行,与请求时中间件执行的顺序正好相反。

    自定义中间件

    Django中的中间件有两种实现方式:基于类的实现方式和基于函数的实现方式,后者更接近于装饰器的写法。装饰器实际上是代理模式的应用,将横切关注功能(与正常业务逻辑没有必然联系的功能,例如:身份认证、日志记录、编码转换之类的功能)置于代理中,由代理对象来完成被代理对象的行为并添加额外的功能。中间件对用户请求和响应进行拦截过滤并增加额外的处理,在这一点上它跟装饰器是完全一致的,所以基于函数的写法来实现中间件就跟装饰器的写法几乎一模一样。下面我们用自定义的中间件来实现对用户进行登录验证的功能。

    1. """
    2. middlewares.py
    3. """
    4. from django.shortcuts import redirect
    5. def check_login_middleware(func):
    6. def wrapper(request, *args, **kwargs):
    7. # 获取请求的资源路径
    8. path = request.path
    9. # 如果请求的资源路径在设定的元组中就表示需要登录验证
    10. if path in ('/vote/praise/', '/vote/criticize/'):
    11. if 'username' not in request.session:
    12. # session中没有username就重定向到登录页
    13. return redirect('login')
    14. return func(request, *args, **kwargs)
    15. return wrapper

    修改配置文件,激活中间件使其生效。

    1. MIDDLEWARE = [
    2. 'django.middleware.security.SecurityMiddleware',
    3. 'django.contrib.sessions.middleware.SessionMiddleware',
    4. 'django.middleware.common.CommonMiddleware',
    5. 'django.middleware.csrf.CsrfViewMiddleware',
    6. 'django.contrib.auth.middleware.AuthenticationMiddleware',
    7. 'django.contrib.messages.middleware.MessageMiddleware',
    8. 'django.middleware.clickjacking.XFrameOptionsMiddleware',
    9. 'debug_toolbar.middleware.DebugToolbarMiddleware',
    10. 'vote.middlewares.check_login_middleware',
    11. ]