tornado批量获取RequestPayload与FormData数据小结

tornado框架相比Django这样的“重武器”轻便了很多,这也意味着tornado没有像Django那样有很多封装好的方法供我们使用,在处理前端请求的数据也是如此。

本文总结一下tornado框架处理前端传来的RequestPayloadFormData两种格式的数据的方法,因为现在很少有人传Query String Parameters这种类型的数据了,所以就不介绍了。

http请求参数之Query String Parameters、Form Data、Request Payload

Query String Parameters

当发起一次GET请求时,参数会以url string的形式进行传递。即?后的字符串则为其请求参数,并以&作为分隔符。

如下http请求报文头:

// General
Request URL: http://foo.com?x=1&y=2
Request Method: GET
// Query String Parameters
x=1&y=2

Form Data

当发起一次POST请求时,若未指定content-type,则默认content-type为application/x-www-form-urlencoded。即参数会以Form Data的形式进行传递,不会显式出现在请求url中。

如下http请求报头:

// General
Request URL: http://foo.com
Request Method: POST
// Request Headers
content-type: application/x-www-form-urlencoded; charset=UTF-8

// Form Data
x=1&y=2

Request Payload

当发起一次POST请求时,若content-type为application/json,则参数会以Request Payload的形式进行传递(显然的,数据格式为JSON),不会显式出现在请求url中。

如下http请求报头:

/ General
Request URL: http://foo.com
Request Method: POST
/ Request Headers
content-type: application/json; charset=UTF-8

// Request Payload
x=1&y=2

tornado处理Request Payload数据

ajax请求可以设置content-type请求头!

前端的请求如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>

title:<input id="t1">
<br><br>
author:<input id="a1">
<br><br>
<button id="button">提交</button>

<script src="{{ static_url('jquery-1.12.4.js') }}"></script>
<script>
    $('#button').click(function () {
        title = $("#t1").val();
        author = $("#a1").val();
        // console.log(title, author);
        $.ajax({
            url: '/index',
            type: 'POST',
            contentType: 'application/json',  //设置请求头,注明编码格式
            data: JSON.stringify({"title": title, "author": author}), // 注意得先序列化才能传递
            success: function (data) {
                console.log(data);
            }
        })
    });
</script>
</body>
</html>

后端tornado代码如下:

# -*- coding:utf-8 -*-
import json
import os.path
import tornado.locale
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options

from logics import handle_payload_args


define("port", default=8006, help="run on the given port", type=int)

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            # 主页
            (r"/index", MainHandler),
        ]
        settings = dict(
            template_path=os.path.join(os.path.dirname(__file__),"templates"),
            static_path=os.path.join(os.path.dirname(__file__),"static"),
            debug=True,
        )
        # 执行父类__init__方法
        tornado.web.Application.__init__(self, handlers,**settings)


from tornado.escape import json_decode

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(
            "index.html",
        )

    def post(self):
        ret = self.request.body # 二进制
        print("request.body:",ret,type(ret)) # request.body: b'{"title":"222","author":"123"}' <class 'bytes'>
        req = json_decode(ret) # 直接用json_decode就可以
        print("req:",req,type(req)) # req: {'title': '222', 'author': '123'} <class 'dict'>

        result = json.dumps(req,ensure_ascii=False) # 设置ensure_ascii=False,防止中文乱码

        self.write(result)

if __name__ == "__main__":
     tornado.options.parse_command_line()
     http_server = tornado.httpserver.HTTPServer(Application())
     http_server.listen(options.port)
     tornado.ioloop.IOLoop.instance().start()

页面调试效果

tornado处理Form Data数据 ***

前端可以用form表单模拟传递数据:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>

<form action="" method="post">
    title:<input type="text" value="" name="title">
    <br><br>
    author:<input type="text" value="" name="author" >
    <br><br>
    <input type="submit">
</form>

</body>
</html>

tornado没有Django封装的POST属性,需要通过get_argument方法一个个获取,太麻烦,可以写一个方法封装一下:

# -*- coding:utf-8 -*-
import json
import os.path
import tornado.locale
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options


define("port", default=8006, help="run on the given port", type=int)

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            # 主页
            (r"/index", MainHandler),
        ]
        settings = dict(
            template_path=os.path.join(os.path.dirname(__file__),"templates"),
            static_path=os.path.join(os.path.dirname(__file__),"static"),
            debug=True,
        )
        # 执行父类__init__方法
        tornado.web.Application.__init__(self, handlers,**settings)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(
            "index.html",
        )
    
    # 自己定义一个批量处理的方法
    def handle_formdata_args(self, query_title):
        result = dict()
        for title in query_title:
            value = self.get_argument(title) # 本质上还是用了get_argument方法获取值
            result[title] = value
        return result


    def post(self):
        ret = self.request.body # 二进制
        print("request.body:",ret,type(ret)) # request.body: b'title=22211&author=12333' <class 'bytes'>
        
        # 提前吧需要查询的参数写进去 —— 注意这里不能直接用json_decode!会报错!
        query_title = ["title", "author"]
        req = self.handle_formdata_args(query_title)
        print("req:",req,type(req)) # req: {'title': '222', 'author': '123'} <class 'dict'>

        result = json.dumps(req,ensure_ascii=False) # 设置ensure_ascii=False 避免中文乱码
        self.write(result)


if __name__ == "__main__":
     tornado.options.parse_command_line()
     http_server = tornado.httpserver.HTTPServer(Application())
     http_server.listen(options.port)
     tornado.ioloop.IOLoop.instance().start()

更方便的方法

# -*- coding:utf-8 -*-
import json
import os.path
import tornado.locale
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options


define("port", default=8006, help="run on the given port", type=int)

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            # 主页
            (r"/index", MainHandler),
        ]
        settings = dict(
            template_path=os.path.join(os.path.dirname(__file__),"templates"),
            static_path=os.path.join(os.path.dirname(__file__),"static"),
            debug=True,
        )
        # 执行父类__init__方法
        tornado.web.Application.__init__(self, handlers,**settings)


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render(
            "index.html",
        )

    def handle_formdata_args2(self):
        result = dict()
        body = self.request.body # 二进制
        print("request.body:", body, type(body))  # request.body: b'title=123123&author=123' <class 'bytes'>
        body_str = body.decode("utf-8")
        print("body_str:",body_str,type(body_str)) # body_str: title=123123&author=123 <class 'str'>
        # 根据规律切割body_str,找出来所有的"key" —— 特别注意:"key"不能有中文!!!否则这个方法不能用!用上面介绍的方法!!!
        query_lst = body_str.split("&",1) # 切割一次防止用户输入的也有&号!
        print(query_lst) # ['title=123123', 'author=123']
        title_lst = []
        for query_str in query_lst:
            title = query_str.split("=",1)[0] # 切割一次!防止用户输入的也有等号!
            title_lst.append(title)
        print(title_lst) # ['title', 'author']
        for title in title_lst:
            value = self.get_argument(title)
            result[title] = value
        return result

    def post(self):
        req = self.handle_formdata_args2()
        result = json.dumps(req,ensure_ascii=False)
        self.write(result)


if __name__ == "__main__":
     tornado.options.parse_command_line()
     http_server = tornado.httpserver.HTTPServer(Application())
     http_server.listen(options.port)
     tornado.ioloop.IOLoop.instance().start()

前端调试效果

在基类的prepare方法中提前处理的思路

我们可以在基类的prepare方法中提前判断一下前端的请求头中contenttype的类型,然后将请求的数据封装成类的属性:

接下来继承这个基类的子类就可以使用body_args这个属性 —— 从前端获取的json格式的数据,来进行接下来的逻辑处理了。 

 

posted on 2020-03-03 22:47  江湖乄夜雨  阅读(1021)  评论(0编辑  收藏  举报