从服务器获得错误的SSE响应

0 投票
1 回答
40 浏览
提问于 2025-04-12 23:46

我正在用Django开发一个项目,已经安装了DRF(Django REST框架)。后台有个进程在生成日志文件。我想在文件的末尾查找特定的字符串。当找到这些字符串时,它们需要在HTML用户页面上显示出来。整个过程都是动态进行的。

这是我的urls.py文件:

    path('api/log-stream/', LogStreamAPIView.as_view(), name='log_stream'),

这是LogStreamAPIView:

class LogStreamAPIView(APIView):
    def get(self, request):
        def stream_log():
            f = subprocess.Popen(['tail', '-F', rcvenot_log], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            p = select.poll()
            p.register(f.stdout)

            while TEST_IN_PROGRESS:
                if p.poll(0.1):
                    new_line_log = f.stdout.readline().decode('utf-8').strip()
                    for target_line in RCV_SCENARIO:
                        if target_line in new_line_log:
                            yield f"event: {new_line_log}\n\n"

        return StreamingHttpResponse(stream_log(), content_type='text/event-stream')

这是用户HTML页面上的脚本:

        <div style="width: 200px; height: 200px; overflow-y: scroll;">
            <ul id="logList"></ul>
        </div>

        <script>
            document.addEventListener("DOMContentLoaded", function() {
                var logList = document.getElementById('logList');

                function updateLog(newLine) {
                    var listItem = document.createElement('li');
                    listItem.textContent = newLine;
                    logList.appendChild(listItem);
                }

                var source = new EventSource('/api/log-stream/');
                source.onmessage = function(event) {
                    updateLog(event.data);
                };
            });
        </script>

我通过API mysite.ru/api/log-stream/ 看到了字符串。一些字符串的格式是这样的:event: 2024.03.22 16:16:13:016[INFO ][Thread-0] ************************* - 2

但是我在HTML页面上看不到这些字符串。

在浏览器控制台中,我看到响应代码是406:GET 127.0.0.1:8000/api/log-stream 406 (Not Acceptable)。响应内容是{"detail":"The \"Accept\" request header could not be satisfied."}

我尝试过用不同的格式发送数据,比如f"event: {new_line_log}\n\n"或者f"data: {new_line_log}\n\n",但是都不行。

我还尝试在return StreamingHttpResponse(stream_log(), content_type='text/event-stream', status=200)中添加状态码,试过status=202,结果也是一样。

这里有一些浏览器控制台的截图。

浏览器控制台截图

浏览器控制台截图

我阅读了SSE(服务器发送事件)的文档,服务器应该返回Content-Type: text/event-stream,但在浏览器控制台中我看到的是Content-Type: application/json。我怀疑问题可能出在这里……

请帮忙找出问题所在。

1 个回答

0

你遇到错误的原因是因为 Django Rest Framework(DRF)在尝试进行内容协商。默认情况下,它会查看 DEFAULT_RENDERER_CLASSES 设置,以确定可以返回哪些内容类型,如果没有匹配的类型,就会返回406错误。

考虑到你的这个视图似乎并没有使用任何 DRF 特有的功能,你可以选择将它转换成一个普通的视图,这样就不需要使用 DRF 了。

另一种解决方案是设置一个自定义内容协商类,这个类会简单地忽略所有的内容协商。以下是 DRF 文档中的一个示例:

from rest_framework.negotiation import BaseContentNegotiation

class IgnoreClientContentNegotiation(BaseContentNegotiation):
    def select_parser(self, request, parsers):
        """
        Select the first parser in the `.parser_classes` list.
        """
        return parsers[0]

    def select_renderer(self, request, renderers, format_suffix):
        """
        Select the first renderer in the `.renderer_classes` list.
        """
        return (renderers[0], renderers[0].media_type)

然后你需要修改你的视图,以使用这个内容协商:

class LogStreamAPIView(APIView):
    content_negotiation_class = IgnoreClientContentNegotiation
    # Rest of your view
    ...

撰写回答