首页 > 解决方案 > Tornado stream_request_body:如何将自定义错误消息写入文档元素?

问题描述

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

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

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

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

任何帮助/提示表示赞赏。我是tornadoJS 的新手,如果问题非常基本,我很抱歉。

使用龙卷风 6.1 版,python 3.9

应用程序.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()

索引.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>

标签: javascripttornado

解决方案


prepare返回还不够的情况下,您需要引发异常以停止处理。

所以你有两个选择:

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

  2. 自己做所有事情:用于self.set_status设置错误状态代码,self.write当场写出您需要的任何内容,然后raise tornado.web.Finish短路请求的处理。

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


推荐阅读