使用 Django 开发项目。 DRF已安装。 后端的一些进程生成日志文件。我正在查看文件末尾的目标字符串。当目标字符串被捕获时,它必须显示在 HTML 用户页面上。一切都在动态中发生。
url.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
中添加状态。一样
这是浏览器控制台的一些屏幕截图。
阅读 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
...