Python Django如何发送连续响应实现命令输出逐行展示
你当前的实现是同步读取命令全量输出后才返回HTTP响应,所以会出现页面长时间加载的问题,要实现实时输出需要改造为流式返回+前端增量渲染,以下是两种可行方案:
方案1:Django原生流式响应 + Fetch API 前端渲染(实现成本最低)
该方案不需要引入额外依赖,适合轻量使用场景。
后端改造
- 替换原来的同步响应为
StreamingHttpResponse,逐行返回命令输出 - 使用
subprocess.Popen替代os.popen,避免命令注入风险,同时支持行缓冲读取
示例代码如下:
import re import subprocess from django.http import StreamingHttpResponse from django.shortcuts import render # 主机合法性校验,避免命令注入 def is_valid_host(host): # 可根据业务需求调整校验规则 return re.match(r'^[a-zA-Z0-9\-\.]+$', host) is not None def nikto_scan(request): host = request.GET.get('host', '') if not host or not is_valid_host(host): return render(request, 'nikto.html', {'error': '请输入合法的主机地址'}) # 生成器函数,逐行返回命令输出 def output_generator(): # 用参数列表形式启动进程,绝对避免命令拼接 proc = subprocess.Popen( ['nikto', '-h', host], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, # 开启行缓冲,读一行返回一行 universal_newlines=True ) for line in iter(proc.stdout.readline, ''): if line.strip(): # 可在此处做HTML转义,避免XSS风险 yield f"{line}<br>" proc.wait() # 构造流式响应 response = StreamingHttpResponse(output_generator()) # 关闭缓存,确保实时输出 response['Cache-Control'] = 'no-cache, no-store, must-revalidate' # 若使用Nginx反代,需要加这个头关闭Nginx侧的缓冲 response['X-Accel-Buffering'] = 'no' return response
前端改造
不要使用同步表单提交,改用Fetch API读取流式内容,逐段追加到页面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Nikto扫描</title> </head> <body> <div class="scan-container"> <input type="text" id="host" placeholder="请输入扫描目标(域名/IP)"> <button onclick="runScan()">开始扫描</button> <div id="scan-output" style="margin-top: 15px; padding: 10px; background: #1e1e1e; color: #00ff00; min-height: 400px; overflow-y: auto;"></div> </div> <script> function runScan() { const host = document.getElementById('host').value.trim(); const outputBox = document.getElementById('scan-output'); outputBox.innerHTML = '扫描启动中...<br>'; fetch(`/your-scan-path?host=${encodeURIComponent(host)}`) .then(res => { const reader = res.body.getReader(); const decoder = new TextDecoder('utf-8'); function readChunk() { reader.read().then(({done, value}) => { if (done) { outputBox.innerHTML += '<br>扫描完成'; return; } outputBox.innerHTML += decoder.decode(value); // 自动滚动到底部 outputBox.scrollTop = outputBox.scrollHeight; readChunk(); }) } readChunk(); }) } </script> </body> </html>
注意事项
- 安全问题:必须严格校验用户输入的主机地址,禁止直接拼接用户输入到命令字符串,否则会存在严重的命令注入漏洞
- 反代配置:如果Django前端有Nginx、Apache等反代服务,需要调整对应扫描接口的配置,关闭响应缓冲,否则还是会等待全量输出后才返回给前端
- 超时配置:nikto扫描时间通常较长,需要调整Web服务器、Django的超时阈值,避免连接被中途断开
方案2:WebSocket 实时推送(适合复杂交互场景)
如果需要支持中途终止扫描、多任务同时扫描等更复杂的交互,可以用Django Channels实现WebSocket通信,核心逻辑如下:
- 前端建立WebSocket连接,发送扫描目标给后端
- 后端收到请求后启动子进程执行nikto命令
- 后端逐行读取命令输出,通过WebSocket推送给前端
- 前端收到消息后直接追加到页面展示
该方案连接稳定性更强,交互灵活性更高,但需要引入Channels、Redis等额外依赖。
内容的提问来源于stack exchange,提问作者Viral
相关产品推荐
相关产品推荐

