【问题标题】:Execute code in Django after response has been sent to the client响应发送到客户端后在 Django 中执行代码
【发布时间】:2011-05-17 20:14:59
【问题描述】:

在我的 Django 应用程序中,我想跟踪响应是否已成功发送到客户端。我很清楚,在 HTTP 这样的无连接协议中没有“无懈可击”的方式来确保客户端已收到(并显示)响应,因此这不是关键任务功能,但我仍然想在最晚可能的时间。响应不会是 HTML,因此来自客户端的任何回调(使用 Javascript 或 IMG 标签等)都是不可能的。

我能找到的“最新”钩子是在中间件列表的第一个位置添加一个实现 process_response 的自定义中间件,但据我了解,这是在构建实际响应并将其发送到客户端之前执行的。响应成功发送后,Django 中是否有任何钩子/事件来执行代码?

【问题讨论】:

    标签: django http django-views httpresponse django-middleware


    【解决方案1】:

    我稍微修改了 Florian Ledermann 的想法......所以有人可以正常使用 httpresponse 函数,但允许他们定义一个函数并将其绑定到特定的 httpresponse。

    old_response_close = HttpResponse.close
    HttpResponse.func = None
    def new_response_close(self):
        old_response_close(self)
        if self.func is not None:
            self.func()
    HttpResponse.close = new_response_close
    

    可以通过以下方式使用:

    def myview():
        def myfunc():
            print("stuff to do")
        resp = HttpResponse(status=200)
        resp.func = myfunc
        return resp
    

    我一直在寻找一种发送响应的方法,然后执行一些耗时的代码......但是如果我可以让后台(很可能是芹菜)任务运行,那么它将使这对我毫无用处.我将在 return 语句之前启动后台任务。它应该是异步的,所以在代码执行完成之前会返回响应。

    ---编辑---

    我终于让 celery 与 aws sqs 一起工作了。我基本上发布了“如何”。看看我在这篇文章中的回答: Cannot start Celery Worker (Kombu.asynchronous.timer)

    【讨论】:

      【解决方案2】:

      如果您经常需要这样做,一个有用的技巧是有一个特殊的响应类,例如:

      class ResponseThen(Response):
          def __init__(self, data, then_callback, **kwargs):
              super().__init__(data, **kwargs)
              self.then_callback = then_callback
      
          def close(self):
              super().close()
              self.then_callback()
      
      def some_view(request):
          # ...code to run before response is returned to client
      
          def do_after():
              # ...code to run *after* response is returned to client
      
          return ResponseThen(some_data, do_after, status=status.HTTP_200_OK)
      

      ...如果您想要一个快速/hacky 的“即发即弃”解决方案,而无需集成适当的任务队列或从您的应用中分离出单独的微服务,这将很有帮助。

      【讨论】:

      • 啊,这与我上面 9 岁且已接受的解决方案有何不同?
      • 本质上不是:P ...但是当您在代码中需要大量“响应式”并且您需要每个人都这样做时,以这种方式编写它会更优雅完全不同的东西,但不必为它们创建单独的类-我想大多数尝试这样做的人最终都会像我一样。我更喜欢这种架构,我认为 ppl 应该看到更多的方法并选择他们喜欢的任何一种,我只是试图将 ppl 更多地推向函数式编程/更少类的代码风格:)
      • 这太棒了!谢谢!
      • 这似乎是一个不错的技巧,但在我的情况下它不起作用。我使用 UWSGI 来处理请求,我相信它可能会在请求关闭时结束该过程。我在回调中放置了日志记录和异常,无法获得任何反馈。
      • 这里的 Response 类应该是什么?
      【解决方案3】:

      我现在要使用的方法是使用 HttpResponse 的子类:

      from django.template import loader
      from django.http import HttpResponse
      
      # use custom response class to override HttpResponse.close()
      class LogSuccessResponse(HttpResponse):
      
          def close(self):
              super(LogSuccessResponse, self).close()
              # do whatever you want, this is the last codepoint in request handling
              if self.status_code == 200:
                  print('HttpResponse successful: %s' % self.status_code)
      
      # this would be the view definition
      def logging_view(request):
          response = LogSuccessResponse('Hello World', mimetype='text/plain')
          return response
      

      通过阅读 Django 代码,我非常确信 HttpResponse.close() 是将代码注入请求处理的最新点。我不确定与上面提到的方法相比,这种方法是否真的能更好地处理错误情况,所以我暂时将这个问题留待解决。

      我更喜欢这种方法而不是 lazerscience 的回答中提到的其他方法的原因是它可以单独在视图中设置,不需要安装中间件。另一方面,使用 request_finished 信号不允许我访问响应对象。

      【讨论】:

      • 发送一个更简单的 json 标志有什么问题?为什么以上都是?
      • 我不明白你所说的“json flag”是什么意思,我也看不到 JSON 以任何方式帮助我解决问题......请详细说明。
      • 可以很好地处理成功的响应,但如果用户中断连接(通过关闭选项卡或点击“停止”按钮),则不会执行 close()。定义一个_del_方法,如果你需要在响应被处理之后做一些事情,即使连接被强行中断了。
      • 我需要做同样的事情,但要记录 API 调用的指标。无法回调,之后我需要访问 httpresponse 或查看代码中的上下文。我这样做的原因是,保存指标有时会使 api 调用所花费的时间加倍。我唯一的方法是使用消息队列和任务服务器(rabbitmq/celery),但我仍然喜欢一种不需要在服务器上进行额外设置的纯 django 方式。
      【解决方案4】:

      我想在谈论中间件时,您正在考虑中间件的 process_request 方法,但还有一个 process_response method 在返回 HttpResponse 对象时被调用。我想这将是您找到可以使用的钩子的最新时刻。

      此外,还有一个 request_finished signal 被解雇。

      【讨论】:

      • 我知道 process_response,但由于预计此方法会返回响应,我相信此时无法发送响应。 request_finished 看起来很有希望,我现在正在研究。
      • 我检查了request_finished,不幸的是,即使处理请求时出现异常,它也会被执行。所以这对我的目的也没有用,因为我只想跟踪成功的响应。
      • 我猜你不会找到比 process_response 更晚的时刻,因为它是在 HttpRequest 完全构造之后调用的,然后它被处理到 Web 服务器......
      • 我不知道你到底想实现什么,但你也可以考虑在客户端有一些 js 发送回一些确认......
      • 如问题所述,响应不是 HTML,因此很遗憾,我无法使用任何客户端回调或 Javascript。
      猜你喜欢
      • 2013-07-19
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多