You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django+DRF实现SSE服务端推送失败,报406错误求助

问题排查与解决

核心原因

DRF的APIView默认启用内容协商机制,会校验请求的Accept头。EventSource发起请求时,Accept头为text/event-stream,但DRF默认的渲染器(如JSONRenderer)不支持该媒体类型,因此返回406错误,同时强制将响应转为JSON格式,导致Content-Type不符合SSE要求。

另外,你的SSE格式存在问题:当前用event: 开头的事件,前端onmessage只能监听未指定event字段的默认消息,无法捕获这类带自定义事件名的内容。

解决方案

方案1:改用普通Django View(推荐)

SSE流不需要DRF的序列化、内容协商等REST API特性,直接使用普通Django View可以彻底绕过DRF的限制:

修改views.py

from django.http import StreamingHttpResponse
import subprocess
import select

def log_stream_view(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:
                        # 遵循SSE标准格式:data: 开头,结尾用两个换行符分隔事件
                        yield f"data: {new_line_log}\n\n"

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

更新urls.py

path('api/log-stream/', log_stream_view, name='log_stream'),

方案2:保留DRF APIView,关闭内容协商

如果必须使用DRF的APIView,可以关闭其内容协商机制,避免媒体类型校验:

from rest_framework.views import APIView
from django.http import StreamingHttpResponse
import subprocess
import select

class LogStreamAPIView(APIView):
    # 关闭DRF内容协商
    content_negotiation_class = None

    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"data: {new_line_log}\n\n"

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

方案3:自定义SSE渲染器(兼容DRF内容协商)

添加支持text/event-stream的自定义渲染器,让DRF识别该媒体类型:

第一步:创建自定义渲染器

from rest_framework.renderers import BaseRenderer

class SSERenderer(BaseRenderer):
    media_type = 'text/event-stream'
    format = 'sse'

    def render(self, data, media_type=None, renderer_context=None):
        return data

第二步:更新APIView

from rest_framework.views import APIView
from django.http import StreamingHttpResponse
import subprocess
import select

class LogStreamAPIView(APIView):
    renderer_classes = [SSERenderer]

    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"data: {new_line_log}\n\n"

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

前端修复

你的HTML代码存在语法错误,需修正div标签的style属性,同时添加错误监听方便调试:

<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);
            // 自动滚动到最新日志
            logList.scrollTop = logList.scrollHeight;
        }

        var source = new EventSource('/api/log-stream/');
        source.onmessage = function(event) {
            updateLog(event.data);
        };
        
        // 监听错误,方便调试
        source.onerror = function(error) {
            console.error('EventSource error:', error);
        };
    });
</script>

内容的提问来源于stack exchange,提问作者Tim-Tam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:32:15