Tornado stream_request_body场景自定义错误返回及执行逻辑问题
问题解决办法
一、Tornado 端 prepare 返回后仍执行 data_received/post 的问题
原因
你使用了@tornado.web.stream_request_body装饰器开启流式请求体接收,prepare方法的return只会结束当前方法的执行,不会主动终止整个请求处理流程,Tornado仍然会持续接收客户端上传的请求体数据,触发data_received回调,最后执行post方法。
解决办法
在错误分支写完响应内容后,调用self.finish()显式终止请求:
def prepare(self): self.content_len = int(self.request.headers.get('Content-Length')) if self.content_len > MAX_STREAMED_SIZE: txt = "Too big file" self.set_status(400) # 明确返回JSON格式头,方便前端解析 self.set_header('Content-Type', 'application/json') self.write(json.dumps({'error': txt})) # 显式终止请求,不会再执行后续的data_received和post self.finish() return
二、JS端 Body has already been consumed 报错+错误提示显示问题
原因
fetch返回的Response对象的body是只能读取一次的流,你在!response.ok分支调用了一次response.json(),后续逻辑又调用了一次,第二次读取时body已经被消费完了就会报错。同时你调用response.json()时没加await,所以控制台会打印pending状态的Promise。
解决办法
只读取一次响应体,错误分支拿到错误信息后直接更新页面元素并终止逻辑:
修改后的JS逻辑如下:
uploadForm.onsubmit = async (e) => { e.preventDefault(); var fileInput = document.getElementById('file'); var fileAttr = fileInput.files[0]; var filename = fileAttr.name; const displayEl = document.getElementById('display'); displayEl.innerHTML = 'Uploading ' + filename; let formData = new FormData(document.getElementById('uploadForm')); try { let response = await fetch(`${window.origin}/upload`, { method: "POST", body: formData, }); // 只读取一次响应体 let result = await response.json(); if (!response.ok) { // 直接用返回的错误信息更新页面元素 displayEl.innerHTML = result.error; // 终止后续成功逻辑 return; } console.log(result); displayEl.innerHTML = '上传完成'; } catch(exception) { console.log(exception); displayEl.innerHTML = '上传发生错误,请重试'; } };
额外优化建议
- 可以在前端先做一次文件大小校验,不用等到请求发往服务端再拦截,减少不必要的带宽消耗:拿到文件对象后判断
fileAttr.size > MAX_SIZE就直接提示错误,不发起fetch请求。 - 服务端保存文件的路径最好指定固定目录,避免生成的文件散落在项目根目录。
内容的提问来源于stack exchange,提问作者jonas
相关产品推荐
相关产品推荐

