龙卷风stream_request_body:如何编写自定义错误消息到文档元素?



我有一个使用stream_request_body上传文件到服务器的tornado应用程序。文件选择是一个HTML表单,其中JSonsubmit函数用于执行上传处理程序。JS函数为async+await fetch。如果用户选择一个超过最大允许大小的文件,那么我在def prepare(self)中使用self.set_status(400)。在这种情况下,我还想发送/写一个文本字符串(self.write('File too big')?),应该显示在文档中的元素作为信息给用户,我该怎么做?

使用我当前的JS脚本,我在浏览器控制台得到一个错误:

Promise { <state>: "pending" }
TypeError: Response.json: Body has already been consumed.

我对tornado服务器设置的另一个问题是,即使我在def prepare(self)函数中有return,当文件大于最大允许时,然后执行def data_receiveddef post(文件实际上上传到服务器),为什么会这样?

感谢任何帮助/提示。我是新的tornado和JS,所以抱歉,如果问题是非常基本的。

使用tornado ver 6.1, python 3.9

application.py

from tornado import version as tornado_version
from tornado.ioloop import IOLoop
import tornado.web
import uuid
import os
import json

MB = 1024 * 1024
GB = 1024 * MB
MAX_STREAMED_SIZE = 1024 #20 * GB
@tornado.web.stream_request_body
class UploadHandler(tornado.web.RequestHandler):
def initialize(self):
self.bytes_read = 0
self.loaded = 0
self.data = b''
def prepare(self):
self.content_len = int(self.request.headers.get('Content-Length'))
if self.content_len > MAX_STREAMED_SIZE:
txt = "Too big file"
print(txt)
self.set_status(400)
# how do I pass this txt to an document element?
self.write(json.dumps({'error': txt}))
# eventhough I have a return here execution is continued
# in data_received() and post() functions 
# Why is that?
return 
def data_received(self, chunk):
self.bytes_read += len(chunk)
self.data += chunk
def post(self):
value = self.data
fname = str(uuid.uuid4())
with open(fname, 'wb') as f:
f.write(value)
data = {'filename': fname}
print(json.dumps(data))
self.write(json.dumps(data))

class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def main():
handlers = [(r'/', IndexHandler), (r'/upload', UploadHandler)]
settings = dict(debug=True, template_path=os.path.dirname(__file__))
app = tornado.web.Application(handlers, **settings)
print(app)
app.listen(9999, address='localhost')
IOLoop().current().start()

if __name__ == '__main__':
print('Listening on localhost:9999')
print('Tornado ver:', tornado_version)
main()

index . html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Upload something!</title>
</head>
<body>
<h1>Upload</h1>
<form id="uploadForm">
<input type="file" name="file" id="file" />
<br />
<input type="submit" value="Upload">
</form>
<p><span id='display'></span></p>
<script>
uploadForm.onsubmit = async (e) => {
e.preventDefault();
var fileInput = document.getElementById('file');
var fileAttr = fileInput.files[0];
console.log(fileAttr);
var filename = fileInput.files[0].name;
console.log(filename);
document.getElementById('display').innerHTML = 
'Uploading ' + document.getElementById("file").value;
let formData = new FormData(document.getElementById('uploadForm'));
try {
let response = await fetch(`${window.origin}/upload`, {
method: "POST",
body: formData,
});
if (!response.ok) {
console.log('error')
console.log(response.json());
// how do I update document.getElementById('display').innerHTML
// with tornado self.write when error response?
}
let result = await response.json();
console.log(result);
document.getElementById('display').innerHTML = 'Finished';
} catch(exception) {
console.log(exception);
} 
};
</script>
</body>
</html>

prepare中,返回是不够的,您需要引发异常来停止处理。

你有两个选择:

  1. 使用提供的功能:在RequestHandler上覆盖write_error以创建自定义错误响应,然后在print之后的prepare中覆盖raise tornado.web.HTTPError(400)[1]

  2. 自己动手:使用self.set_status设置错误状态码,self.write当场写出你需要的任何东西,然后raise tornado.web.Finish短路请求的处理。

使用您的代码,您基本上只需要将prepare中的return替换为raise tornado.web.Finish()。显然,如果你要在多个地方执行此操作,那么使用#1是有意义的,但如果你只有现在的脚本,#2就可以了。

最新更新