通过 Qt 发送 PUT 请求时错误的请求类型 PUT,适用于 curl

Bad request type PUT when sending PUT request through Qt, works with curl

我已经 class 编辑了 BaseHTTPRequestHandler 并实现了 do_GET()do_POST()do_PUT()

在我对 Qt 应用程序进行测试之前,我认为一切正常。 GET 请求有效,服务器发送一个 XML 文档,其中包含应用程序处理的一些数据。 POST 请求也有效,用于通过 REST 生成测试用例。

至于 PUT,事情看起来很奇怪。

这是服务器端的 PUT 处理程序:

def do_PUT(self):
        """
        Processes PUT request. It can be tested using wget, curl or any
        other tool with support for http PUT. Use this to send reports
        about the current status of device and all of its slave devices

        Example:
            curl -X PUT -d "status=downloading" http://127.0.0.1:8090/so/updateProgress
        """
        print('Processing PUT request...')

        params_parsed = urlparse(self.path)
        params = parse_qs(params_parsed.query)
        if len(params) > 0 or '/so/updateProgress' not in params_parsed.path:
            print('Use "/so/updateProgress" to report on update progress')
            self.__set_headers(data_type='text/html')
            self.send_error(501)
            return

        self.__set_headers(data_type='text/xml')
        report_raw = self.rfile.read(int(self.headers.getheader('Content-length')))
        print('Received report')
        print('Parsing report...')
        # Generate XML from the raw data and validate it
        report = SoRequestHandler.Report.from_xml(report_raw)
        print('Parsing of report complete')
        report.write_to_file(log_path='report_log', append=True)
        self.send_response(200)

在我的 Qt 应用程序中,我有一个名为 ReportPublisher 的 class,它从它连接的所有设备(通过 MQTT)获取一些报告,将报告聚合成一个报告并发送它发送到服务器,服务器将其记录在一个文件中:

void ReportPublisher::publishHttpReport(HttpReport report)
{
    QString reportXml = report.xml().toString();
    if (reportXml.isEmpty())
    {
        LOG(ERROR) << "Something went wrong with the generation of HTTP report";
        return;
    }

    LOG(INFO) << "Generated report for SO server: " << reportXml;
    QByteArray reportRaw = reportXml.toUtf8();

    QUrl target(this->urlServer);
    target.setPath(this->urlPath);
    QNetworkRequest req(target);
    req.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/xml"));
    this->reply = this->nam->put(req, reportRaw);

    // TODO Read back response (expected code 200)?
}

我必须说实话。我从来没有在 Qt 中做过 PUT 请求,到目前为止我所做的都是 GET 请求,所以上面的代码中可能存在一些非常基本但很容易发现的错误。

服务器收到的数据是这样的:

<updateProgress xmlns='service.so.de/so'>
    <deviceTypeId>...</deviceTypeId>
    <packageId>...</packageId>
    <version>...</version>
    <status>...</status>
</updateProgress>

如果我这样使用curl

root@obunit:~$ curl -X PUT -i 'http://192.168.120.61:8090/so/updateProgress' -d "<updateProgress xmlns='service.so.de/so'><deviceTypeId>...</deviceTypeId><packageId>...</packageId><version>...</version><status>...</status></updateProgress>"

其中 192.168.120.61:8090 是 IP 地址和服务器所在的端口 at/listening 对于传入请求我没有问题。

然而,对于来自我的 Qt 应用程序的数据,我得到了

192.168.120.172 - - [11/Apr/2018 15:32:37] code 400, message Bad HTTP/0.9 request type ('PUT')
192.168.120.172 - - [11/Apr/2018 15:32:37] "PUT  HTTP/1.1" 400 -

在我的日志中(192.168.120.172 是我的软件所在系统的 IP 地址 运行。

据我所知code 400表示语法无效,这可能是由于以下两个原因(至少我现在能想到的):

我尝试使用 QDomDocument::toByteArray(int i) 将我生成的 XML 文档转换为 QByteArray。我也尝试过(正如您在代码中看到的那样)将文档转换为 QString,然后转换为 UTF-8 QByteArray,但我无法让我的服务器处理数据。

更奇怪的是(正如你在我的 do_PUT() 中看到的那样)实现我的 PUT 处理程序从打印一条消息开始,该消息从未出现在日志中因此问题来自代码中更深的地方BaseHTTPRequestHandler.

问题很简单,但很烦人。

按照@MrEricSir 的建议,我确实使用了 Wireshark,只是为了发现它得到了一些奇怪的 TCP 重传。重传通常是由于网络拥塞而发生的,在我的情况下这是不可能的,但它确实表明网络问题与我发送的 XML 内容无关。

所以我再次开始了我的远程调试会话,并仔细查看了我发出 PUT 请求的位置。就在那里!基础 URL (IP:PORT) 和路径之间缺少 /。所以而不是

PUT 192.168.120.61:8090/so/updateProgress

我在做

PUT 192.168.120.61:8090so/updateProgress

最终导致 PUT 请求根本不成功(因此代码为 400)。

给以后阅读本文的任何人一个提示 - 每当您使用 QUrl::setPath(QString)

手动设置该路径时
QUrl target(this->urlServer);
target.setPath(this->urlPath);

始终 确保您作为路径传递的字符串以 / 开头,因为 Qt 不会添加一个!