中间件

Django中间件

  • 概念:Django中一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出无侵入的开发方式,增强了Django框架的健壮性。

Django内置的六个中间件方法

  1. 类似于Flask框架的请求钩子
1. 初始化
  • 无需要任何参数、服务器响应第一次请求的只调用一次

    def __init__():
        pass
    
2. 处理请求前
  • 在每个请求上调用,返回None 或 HttpResponse,需要接受请求对象

    def process_request(request):
      pass
      # 返回None / HttpResponse继续往下传递
        return None/HttpResponse
    
3.处理视图前
  • 在每个请求上调用, 返回None 或者 HttpResponse。

  • 接收请求参数:请求对象、请求的视图函数、请求视图参数

    def process_view(request, view_func, view_args, view_kwargs):
        pass
        return HttpResponse
    
4.处理模版响应前
  • 在每个请求上调用,返回实现了的render 方法的响应对象。

  • 请求参数:请求对象、响应对象

    def process_templates_response(request, response):
        pass
        return
    
5.处理响应后
  • 所有的响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse

  • 请求参数:请求对象、响应对象

    def process_response(request, response):
        pass
        return HttpResponse
    
6.异常处理
  • 当视图中抛出异常时候调用,在每个请求视图上调用,返回一个HttpResponse对象

  • 请求参数:请求对象、抛出异常

    def process_response(request, exception):
        pass
      return HttpResponse
    

自定中间件的定义方法

1.自定义
  • 定义一个中间件工厂函数、然后返回一个可以别出调用的中间件

  • 中间件工厂函数需要接受一个可以调用的get_response对象

  • 返回的中间件也是一个可以调用的对象,并像视图一样需要接受一个request对象参数,返回一个response对象

    # 本质:中间件就是一个装饰器,get_response:就是视图函数
    def my_middleware(get_response):
    
        # 1.仅在Django第一次配置初始化的时候执行一次,在DEBUG模式下会执行两次init
        print('init')
    
        def middleware(request):
            # 2.每个请求匹配到URL处理视图函数之前被调用
            print('before request 被调用前')
    
            response = get_response(request)
    
            # 3.每个请求处理视图函数之后被调用
            print('after request 被调用后')
            return response
        return middleware
    
  • 定义好的中间件需要在settings.py文件中注册

  • 定义多个中间件执行顺序:请求视图前,中间由上至下执行。请求视图后,中间件由下至上执行。

2.使用场景
  • 异常处理的方法的重写,因为原有的中间件不能捕获数据库错误

    # 添加Django REST framework的默认异常处理方法,redis和mysql异常
    from rest_framework.views import exception_handler as drf_exception_handler
    import logging
    from django.db import DatabaseError
    from redis.exceptions import RedisError
    from rest_framework.response import Response
    from rest_framework import status
    
    # 获取在配置文件中定义的logger,用来记录日志
    logger = logging.getLogger('django')
    
    
    def exception_handler(exc, context):
        """
        自定义异常处理
        :param exc: 异常
        :param context: 抛出异常的上下文
        :return: Response响应对象
        """
        # 调用drf框架原生的异常处理方法
        response = drf_exception_handler(exc, context)
    
        if response is None:
            view = context['view']
    
            if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
                # 数据库异常
                logger.error('[%s] %s' % (view, exc))
                response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
    
        return response
    

Flask 请求钩子

1. 概念

  1. 在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如
  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;
  1. 为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
  2. 请求钩子是通过装饰器的形式实现

2. Flask四种请求钩子

  1. before_first_request

    # 在第一次请求之前会调用(在发起请求之前就执行该请求钩子)
    # 应用地方:程序的初始化操作(一些准备工作,数据的链接请求或扫尾工作)
    @app.before_first_request
    def before_first_request():
        print('before_first_request') 
    
  2. before_request

    # 在每一次请求之前会调用
    # 应用:用户的权限验证
    @app.before_request
    def before_request():
        #  在这里可以做一些判断,这里使用return 后面的都不会执行(视图函数都不会进入)
        print('before_request')
        # if ....
        # return 'hehe'
        #  return判断以后,后面的接口就不会进入,做用户的黑名单校验
    
  3. after_request

    # 在每次请求执行之后执行
    @app.after_request
    def after_request(response):
        print('after_request')
        # 这里可以对响应对象做处理--->一般都是对响应做处理
        # 对所有返回的json数据进行处理(所有的都处理掉)
        response.headers["Content-Type"] = "application/json"
        return response
    
  4. teardown_request

    # 在每次请求执行的最后执行,如果服务器出现错误,这里也可以获取
    @app.teardown_request
    # @app.errorhandler
    def teardown_request(error):
        # 可以记录错误
        # 以后可能会用到请求数据库的自动提交
        print('teardown_request:%s' % error)
    

Spider中间件

1. 爬虫中间件

  1. 当爬虫创建时回调

    @classmethod
    def from_crawler(cls, crawler):
        """当爬虫被创建是回调"""
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s
    
  2. 进入爬虫回调函数

    def process_spider_input(self, response, spider):
        """
            1。响应对象 进入 爬虫时 回调
            2。返回None 或者 抛出异常--》None时继续往下执行
            """
        # Called for each response that goes through the spider
        # middleware and into the spider.
    
        # Should return None or raise an exception.
        return None
    
  3. 爬虫返回数据到引擎回调

    def process_spider_output(self, response, result, spider):
        """
            当爬虫 返回 数据给引擎的时候 回调
            Request, dict or Item objects
            :return:必须返回 请求对象, 字典对象, item数据模型对象
            """
        # Called with the results returned from the Spider, after
        # it has processed the response.
    
        # Must return an iterable of Request, dict or Item objects.
        for i in result:
            yield i
    
  4. 处理引擎首次获取start_url 构建 request 对象回调

    def process_start_requests(self, start_requests, spider):
        """处理引擎首次 获取 start_url构建 request 对象时 回调"""
        # 也要经过爬虫中间件
        # Called with the start requests of the spider, and works
        # similarly to the process_spider_output() method, except
        # that it doesn’t have a response associated.
    
        # Must return only requests (not items).
        for r in start_requests:
            yield r
    

2. 下载中间件

  1. spider创建 初始化

    @classmethod
    def from_crawler(cls, crawler):
        """当spider 被创建时,回调此方法,一般初始化方法"""
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s
    
  2. 引擎把请求 request 对象传递给下载器请求

    def process_request(self, request, spider):
        """
            当引擎把请求 request 传递给 下载器时候回调
            返回:
            None:继续处理请求
            Response:把响应对象还给引擎,并且引擎会把数据传递给爬虫
            Request:把请求对象还给引擎, 引擎会重新放入请求调度器队列中
            raise IgnoreRequest:触发异常处理,会回调函数process_exception来处理异常
            """
    
        # Called for each request that goes through the downloader
        # middleware.
    
        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None
    
  3. 下载器,完成请求返回引擎响应对象

    def process_response(self, request, response, spider):
        """当下载器完成,返回给引擎响应对象时 回调
            返回:
            Response:把响应对象还给引擎,并且引擎会把数据传递给爬虫
            Request:把请求对象还给引擎, 引擎会重新放入请求调度器队列中, 继续发起请求
            """
        # Called with the response returned from the downloader.
    
        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response
    
  4. 处理异常

    def process_exception(self, request, exception, spider):
        """处理异常函数
            None:继续处理这个异常,向下一个中间件处理异常
            Response:停止异常传递
            Request:停止异常传递"""
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.
    
        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容