39

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

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

4

4 回答 4

33

我现在要使用的方法使用 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 信号不允许我访问响应对象。

于 2010-11-30T13:19:06.693 回答
27

如果你需要做很多,一个有用的技巧是有一个特殊的响应类,比如:

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 的“即发即弃”解决方案,而无需集成适当的任务队列或从您的应用程序中分离出单独的微服务,这将很有帮助。

于 2019-03-13T10:31:20.720 回答
3

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

此外,还有一个request_finished信号被触发。

于 2010-11-30T12:09:54.150 回答
0

我稍微修改了 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 一起工作。我基本上发布了“如何”。查看我在这篇文章中的回答: 无法启动 Celery Worker (Kombu.asynchronous.timer)

于 2021-09-29T19:24:36.510 回答