URL解码和反序列化失败

时间:2019-06-15 13:01:37

标签: python-3.x flask protobuf-python

使用协议缓冲区,我将数据序列化并通过将其作为参数添加到发送到Flask的HTTP GET请求中进行传输。数据反序列化有时会失败,具体取决于参数的内容(例如,如果url参数包含“%F0”)。

我尝试使用不同的字符集进行编码/解码,还尝试向请求设置content-type的application / x-protobuf添加适当的标头。

这是烧瓶处理传入的GET请求的代码。

def _ai_request_stub(min_params: List[str], on_parameter_available: Callable[[], Response]) -> Response:
    """
    This stub is designed for GET requests.
    """
    from flask import request
    missing_params = list(filter(lambda p: p not in request.args, min_params))
    if missing_params:
        return Response(response="The request misses one of the parameters [\"" + "\", ".join(missing_params) + "\"]",
                        status=400, mimetype="text/plain")
    else:
        return on_parameter_available()


@app.route("/ai/control", methods=["GET"])
def control():
    def do() -> Response:
        from aiExchangeMessages_pb2 import Control
        from flask import request
        control_msg = Control()
        control_msg.ParseFromString(request.args["control"].encode())
        return Response(response="Fine", status=200, mimetype="application/x-protobuf")

    return _ai_request_stub(["control"], do)

这是创建和发送GET请求的代码。

class AIExchangeService:
    from aiExchangeMessages_pb2 import SimStateResponse, DataRequest, DataResponse, Control, Void, AiID
    from typing import Dict, AnyStr, Any

    def __init__(self, host: str, port: int):
        self.host = host
        self.port = port

    [...]

    def _do_get_request(self, address: str, params: Dict[str, AnyStr]) -> HTTPResponse:
        """
        :return: The response object of the request
        """
        from urllib.parse import urlencode
        from http.client import HTTPConnection
        connection = HTTPConnection(host=self.host, port=self.port)
        print(params)
        connection.request("GET", address + "?" + urlencode(params),
                           headers={"content-type": "application/x-protobuf; charset=utf-8"})
        return connection.getresponse()

    def control(self, commands: Control) -> Void:
        response = self._do_get_request("/ai/control", {"control": commands.SerializeToString()})
        if response.status == 200:
            print("Controlled")
        else:
            print(response.status)
            print(response.reason)

    [...]

这是显示Control对象结构的原缓冲代码。

message AiID {
    [...]
}

message Control {
    message AvCommand {
        double accelerate = 1;
        double steer = 2;
        double brake = 3;
    }

    enum SimCommand {
        RESUME = 0;
        FAIL = 1;
        CANCEL = 2;
    }
    AiID aid = 1;
    oneof command {
        AvCommand avCommand = 2;
        SimCommand simCommand = 3;
    }
}

方法commands.SerializeToString()的第二个片段中的调用control(...)产生b'\n\x13\n\n\n\x08fancySid\x12\x05\n\x03ego\x12\t\t\x00\x00\x00\x00\x00\x00\xf0?'。在方法address + "?" + urlencode(params)的第二个片段中对_do_get_request(...)的求值产生了输出/ai/control?control=%0A%13%0A%0A%0A%08fancySid%12%05%0A%03ego%12%09%09%00%00%00%00%00%00%F0%3F,该输出似乎是相同的,但已经过urlencoded。

在方法request.args["control"]的第一个代码段中将此GET请求发送到烧瓶control()时,将产生'\n\x13\n\n\n\x08fancySid\x12\x05\n\x03ego\x12\t\t\x00\x00\x00\x00\x00\x00�?',它不再是相同的序列化字符串。尝试反序列化此字符串失败,错误为google.protobuf.message.DecodeError: Error parsing message

如何使flask正确读取参数?

1 个答案:

答案 0 :(得分:0)

GET请求不是用于传送巨大的原始二进制数据的。相反,必须使用POST请求。 确实,发布请求的效果很好。