django基础(15)_django中间件

WSGI协议

服务器网关接口, 就是一种规范,实现web应用程序和web服务器之间的解耦
作用: 
    1. 封装socket 
    2. 将http请求解包成request请求数据
    3. 返回的时候,按着http协议封装成响应数据

django的请求生命周期

 

 

 

中间件  

1. 介于request和response之间, 也就是在全局上改变django的输入和输出

2. django自带了7个中间件, 每次请求和响应之间都要经过中间件

中间件的执行流程

 

自定义中间件

自定义中间件,一般我们会在应用中创建一个utils文件夹,然后可以自定义一个py文件,比如 middlwares.py

 

 

from django.shortcuts import redirect,HttpResponse,render
from django.utils.deprecation import MiddlewareMixin

# 登录认证中间件
class LoginAuth(MiddlewareMixin):
    # 白名单
    white_list = ['/login/', '/register/']


    # 对请求处理用process_request, 如果请求通过了处理,就return None,如果没有通过直接return HttpResponse对象
    def process_request(self,request):

        current_path = request.path
        if current_path not in self.white_list:
            status = request.session.get('is_login')
            if not status:
                # return HttpResponse('ok')  #如果return 的是一个HttpResponse对象,那么中间件执行到这里,直接返回
                return redirect('login')

        print('请求它来啦!!!')

 

3 在settings配置文件中加上我们定义的类的路径

MIDDLEWARE = [

    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',  # 封装session功能的 ,request.session['xx'] ='oo'
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    # 尽量将自己定义的中间件放到最后
    'app01.utils.mymiddleware.LoginAuth',
]

 

  • process_request  在请求来的时候执行
class Md1(MiddlewareMixin):
    def process_request(self, request):
        print(request.META['REMOTE_ADDR']) # 获取当前请求的ip  127.0.0.1

 

  • process_response 在响应的时候执行

 

 

from django.utils.description import MiddlewareMixin
class Md1(MiddlewareMixin):
    def process_response(self, request, response):
        print('md1_response')
        return response  # 必须返回response
  • process_view   

它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

 

 

 

 

class Md1(MiddlewareMixin):

    def process_request(self, request):
        print(request.META['REMOTE_ADDR'])
    def process_response(self, request, response):
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        :param request: 请求对象
        :param view_func:  要执行的函数
        :param view_args: url中携带的参数(无形分组)
        :param view_kwargs: url中携带的参数(有名分组)
        :return:
        """
        print('md1--process--view')
        print(view_func, view_args, view_kwargs)
  •  process_exception  捕获异常,有异常时执行
  •       process_exception(self, request, exception)

          该方法两个参数:

          一个HttpRequest对象

          一个exception是视图函数异常产生的Exception对象。

          这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

     

     若没有异常的话, process_exception方法不执行

def index(request, year):
    print('index', year)
    raise ValueError('error')

    return HttpResponse('index ok')
class Md2(MiddlewareMixin):
    def process_request(self, request):
        # print('md2 request')
        return None
    def process_response(self, request, response):
        # print('md2 response')
        return response

    def process_exception(self, request, exception):
        print(exception, type(exception)) # error <class 'ValueError'>
      
        # 统一做异常处理
        if isinstance(exception, ValueError):
            return HttpResponse('视图函数报错啦?', status=500)
  •  process_template_response(用的比较少)

      process_template_response(self, request, response)

      它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。

      process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。

 

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))

    def process_template_response(self, request, response):
        print("MD1 中的process_template_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response

 

 

def index(request):
    print("app01 中的 index视图")
  #raise ValueError('出错啦') 
    def render():
        print("in index/render")  
        #raise ValueError('出错啦') #至于render函数中报错了,那么会先执行process_template_response方法,然后执行process_exception方法,如果是在render方法外面报错了,那么就不会执行这个process_template_response方法了。
        return HttpResponse("O98K") #返回的将是这个新的对象
    rep = HttpResponse("OK")
    rep.render = render
    return rep

 

中间件的实际应用

  

    1、做IP访问频率限制

      某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

    2、URL访问过滤

      如果用户访问的是login视图(放过)

      如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

 

 中间件版登陆验证

 

中间件版的登录验证需要依靠session,所以数据库中要有django_session表。

    urls.py

复制代码
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/$', views.index),
    url(r'^login/$', views.login, name='login'),
]
复制代码

    views.py

复制代码
from django.shortcuts import render, HttpResponse, redirect


def index(request):
    return HttpResponse('this is index')


def home(request):
    return HttpResponse('this is home')


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "Q1mi" and pwd == "123456":
            # 设置session
            request.session["user"] = user
            # 获取跳到登陆页面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳转回登陆之前的URL
            if next_url:
                return redirect(next_url)
            # 否则默认跳转到index页面
            else:
                return redirect("/index/")
    return render(request, "login.html")
复制代码

    login.html

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录页面</title>
</head>
<body>
<form action="{% url 'login' %}">
    <p>
        <label for="user">用户名:</label>
        <input type="text" name="user" id="user">
    </p>
    <p>
        <label for="pwd">密 码:</label>
        <input type="text" name="pwd" id="pwd">
    </p>
    <input type="submit" value="登录">
</form>
</body>
</html>
复制代码

    middlewares.py

复制代码
class AuthMD(MiddlewareMixin):
    white_list = ['/login/', ]  # 白名单
    balck_list = ['/black/', ]  # 黑名单

    def process_request(self, request):
        from django.shortcuts import redirect, HttpResponse

        next_url = request.path_info
        print(request.path_info, request.get_full_path())

        if next_url in self.white_list or request.session.get("user"):
            return
        elif next_url in self.balck_list:
            return HttpResponse('This is an illegal URL')
        else:
            return redirect("/login/?next={}".format(next_url))
复制代码

    在settings.py中注册

复制代码
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'middlewares.AuthMD',
]
复制代码

 

    AuthMD中间件注册后,所有的请求都要走AuthMD的process_request方法。

    访问的URL在白名单内或者session中有user用户名,则不做阻拦走正常流程;

    如果URL在黑名单中,则返回This is an illegal URL的字符串;

    正常的URL但是需要登录后访问,让浏览器跳转到登录页面。

    注:AuthMD中间件中需要session,所以AuthMD注册的位置要在session中间的下方。

 

 

 

posted @ 2020-12-04 18:27  死里学  阅读(86)  评论(0)    收藏  举报