【问题标题】:How to use QNetworkManager for REST api?如何将 QNetworkManager 用于 REST api?
【发布时间】:2015-05-11 16:34:55
【问题描述】:

我想创建一个通过 REST API 访问数据的类,例如:

class MeteoStation{
   int getLatestTemperature();
   int getLatestPessure();
   private:
   QNetworkManager nmng;
}

我该如何实现这些方法?通常我使用类似的东西:

   int MeteoStation::getLatestTemperature(){    
      ...
      QEventLoop eventLoop;
      connect(&m_nam,SIGNAL(finished(QNetworkReply*)),&eventLoop,SLOT(quit()));
      QNetworkReply *reply = m_nam.get( req );
      eventLoop.exec();
      reply->readAll()
      ...
    }

但是由于不推荐使用内部QEventLoop,我应该如何查看响应属于谁?

MeteoStation::MeteoStation(){
   connect(&nmam, SIGNAL(finished(QNetworkReply*)),
                     this, SLOT(parseNetworkResponse(QNetworkReply*)));
}

void MeteoStation::parseNetworkResponse( QNetworkReply *finished )
{
   QByteArray data = finished->readAll();
...

是的,保存类线程会很好。你是如何在你的代码中解决这个问题的?

使调用与以下内容同步有多糟糕:

QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));

QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
    QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

if (reply->error() != QNetworkReply::NoError) {
    qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();

【问题讨论】:

  • "我应该如何查看回复属于谁?" - 将指向创建的 QNetworkReply 对象的指针返回给 getLatestTemperature 的调用者。然后调用者可以连接到它的finished() 信号。如果你想封装错误处理或回复解析,你可以将它包装到另一个命令模式样式类中。
  • 连接到 QNetworkReply 对象是个好主意。它正在解决问题:“我应该如何查看响应属于谁?”现在在等待响应?想象一下,int MeteoStation::getLatestTemperature() 最初是从某个内存映射寄存器中读取温度值,现在应该从 REST 中获取值。我可以以某种方式修改 MeteoStation 类(在库中)还是必须重新设计应用程序?
  • 我认为在同步 API 后面隐藏异步行为是徒劳且容易出错的:1)使用本地事件循环是许多弊端的根源,应该避免 2)你可能不想在等待网络响应进入时阻止调用者(例如在 UI 线程中)。我认为您应该重新设计应用程序以假设异步通信。从长远来看,它将防止许多严重的头痛。
  • 嗯,我已经在 Qt PostgreSQL 和 C++ CouchDB 驱动程序中挖掘出来,它们都阻塞了查询结果的调用者。循环中的 Postgresql 和 curl 的 Pillowtalk。同步调用可能是邪恶的,但它们在 Qt 代码中也被广泛使用。重新设计整个应用程序是不可能的。例如,我不能简单地将类 init() 方法分解为 startInit()、continueInitWithTemperature()、continueInitWithPressure() 以及每次我询问 REST 服务器时。

标签: c++ qt rest network-programming


【解决方案1】:

我已经使用 QCoreApplication::processEvents() 解决了我的问题。响应在 ms 内,我能够实现接近 libcurl 的功能。

QNetworkRequest req(url);
QScopedPointer<QNetworkReply> reply(nam.get(req));

QTime timeout= QTime::currentTime().addSecs(10);
while( QTime::currentTime() < timeout && !reply->isFinished()){
    QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

if (reply->error() != QNetworkReply::NoError) {
    qDebug() << "Failure" <<reply->errorString();
}
QByteArray data = reply->readAll();

【讨论】:

    【解决方案2】:

    Qt 文档应该提供您需要的所有信息。 您创建一个名称,连接完成的信号,发送请求。

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(parseNetworkResponse(QNetworkReply*)));
    
    manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
    

    检测回复属于哪个请求应该不会太难。回复包含网址。可能会有所不同,但没有那么不同:

    ...但由于各种原因,它可能会有所不同(例如, 文件路径被设为绝对或规范)。

    QUrl QNetworkReply::url() const

    返回下载或上传内容的 URL。请注意, URL 可能与原始请求的 URL 不同。

    【讨论】:

    • 我明白你从响应中解析请求 url 的观点,但这看起来不是一个好的方法。你能指导我如何制作int MeteoStation::getLatestTemperature()的方法吗?或者(getter 方法)是错误应用设计的标志,应该总是void MeteoStation::requestLatestTemperature() 那样完成,然后在插槽中处理响应?
    • 没什么可帮助的。我不知道你的数据。一般来说,如果你使用我或弗兰克奥斯特菲尔德的方法,这并不重要。数据一到,处理方式都是一样的。 int MeteoStation::getLatestTemperature() 很难用于异步请求。 void MeteoStation::requestLatestTemperature() 是(恕我直言)唯一明智的选择。
    • 数据在 CouchDB 服务器中,速度非常快。使用 MySQL 本机库时,我也没有从服务器获得响应作为信号。我想要的只是让 HTTP 请求同步。
    • 无论您的 CouchDB 响应有多快,它们仍然是异步的。将它们锤成同步设计是一件令人头疼的事情。您尝试使用本地事件循环进行此操作...坏主意。我不知道,如何以干净的方式做这样的事情。而且您无法将 CouchDB 服务器与 MySQL 服务器进行比较……完全不同的架构。
    • 你是对的,基本上每次对外部程序数据的访问都是异步的:数据库、samba 共享。甚至文件系统也是。对于这一切,我们有同步(寻找)方法。那么有没有办法同步、干净地进行 HTTP 请求呢?
    猜你喜欢
    • 2016-11-27
    • 2010-10-20
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-21
    相关资源
    最近更新 更多