Получение неверного ответа SSE от сервера

Разработка проекта с помощью Django. Установлен DRF. Некоторые процессы в бэкенде генерируют лог-файл. Я ищу в конце файла целевые строки. Когда целевая строка была поймана, она должна быть показана на 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’s , status=200) и status=202. То же самое

Вот несколько снимков экрана консоли браузера.

screen shots of browser console

screen shots of browser console

Почитайте документацию SSE, сервер должен отвечать Content-Type: text/event-stream, а в консоли браузера я вижу Content-Type: application/json. Я подозреваю, что ошибка может быть здесь...

Пожалуйста, помогите разобраться с проблемой

Причина, по которой вы получаете ошибку, заключается в том, что Django Rest Framework пытается выполнить согласование содержимого. По умолчанию он смотрит на параметр 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
    ...
Вернуться на верх