前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Qt开源网络库[3]-原理篇上

Qt开源网络库[3]-原理篇上

作者头像
Qt君
发布2019-07-15 15:26:49
1.1K0
发布2019-07-15 15:26:49
举报
文章被收录于专栏:跟Qt君学编程跟Qt君学编程

上一篇介绍到接口的使用,本篇主要讲述的是该网络库的HttpService与HttpRequest原理与实现。对QNetworkAccessManager封装和管理Http请求。采用builder设计模式,这样在多参数情况下可以灵活运用。

由于网络请求是异步发生,所以需要信号与槽的配合。HttpResponse主要为转发QNetworkReply事件。HttpResponse由于支持槽函数的类型自动推导(根据参数类型的不同自动绑定对应数据属性的信号)。

HttpRequest类主要是基于QNetworkRequest类,封装了请求相关的参数。

1. builder设计模式的应用,先看下列例子

代码语言:javascript
复制
HttpService().get("http://mobilecdn.kugou.com/api/v3/search/song")
             .queryParam("format", "json")
             .queryParam("keyword", musicName)
             .queryParam("page", page)
             .queryParam("pagesize", pageSize)
             .queryParam("showtype", 1)
             .onResponse(responeReceiver, responeSlot)
             .onError(errorReceiver, errorSlot)
             .exec();

Http请求

(1) 可以看出这是一种链式编程风格,使用起来非常清晰明了。在多参数输入的情况下很适合该做法适合。builder一个重要特征是返回自己,而下一个调用者也同样返回自己,直到执行结束。在Qt库常用QString的arg函数就是通过不断调用arg并返回自己达到追加内容的功能;

(2) 如何不使用builder模式,则设置参数需要一条一条设置,这样会使得内容很臃肿;

(3) 以下为get请求返回HttpRequest, 而HttpRequest类设置参数都会返回自己对象。

代码语言:javascript
复制
HttpRequest HttpService::get(const QString &url)
{
    return HttpRequest(QNetworkAccessManager::GetOperation, this).url(url);
}

get函数

代码语言:javascript
复制
HttpRequest &url(const QString &url);
HttpRequest &header(const QString &key, const QVariant &value);
HttpRequest &headers(const QMap<QString, QVariant> &headers);

HttpRequest &queryParam(const QString &key, const QVariant &value);
HttpRequest &queryParams(const QMap<QString, QVariant> &params);

HttpRequest &userAttribute(const QVariant &value);
HttpRequest &jsonBody(const QVariant &jsonBody);
HttpRequest &onResponse(const QObject *receiver, const char *slot, HttpResponse::SupportMethod type = HttpResponse::AutoInfer);
HttpRequest &onError(const QObject *receiver, const char *slot);
HttpResponse *exec();

HttpRequest函数

(4) 最后的exec代表执行函数,调用则生效发送网络请求,另外exec返回HttpResponse对象,由于HttpResponse继承QNetworkReply类,还可以对HttpResponse进行二次处理。

2. HttpRequest主要是封装QNetworkRequest类,但在封装上还是有些技巧,比如:

(1) jsonBody函数接收的参数为QVariant, 在Qt库里QVariantMap与QJsonObject都可以转换为json格式数据,通过判别转换即可得到QJsonObject数据。另外,支持发送json数据的Http接口只有post与put, 而使用get则会被忽略。

代码语言:javascript
复制
HttpRequest &HttpRequest::jsonBody(const QVariant &jsonBody)
{
    if (jsonBody.type() == QVariant::Map) {
        m_jsonBody = QJsonObject::fromVariantMap(jsonBody.toMap());
    }
    else if (jsonBody.typeName() ==  QMetaType::typeName(QMetaType::QJsonObject)) {
        m_jsonBody = jsonBody.toJsonObject();
    }

    return *this;
}

jsonBody函数

(2) onResponse与onError作用是通过传入响应槽函数,当请求完成或返回失败状态码则会调用对应的槽,这里面涉及到一套信号与槽的相关知识。在调用onResponse或onError时候,对应的接收者"receiver"(对象)与接收者槽函数"slot"会加进m_slotsMap变量里面。

代码语言:javascript
复制
QMultiMap<QString, QMap<QString, const QObject *>> m_slotsMap;

m_slotsMap变量

<2.1>值得一提的是m_slotsMap不仅仅是存放接收者与接收槽函数,还具有保存了HttpResponse::SupportMethod变量的值,这一个值非常重要,就是前面所说到的通过它可以让系统识别到需要什么返回。

代码语言:javascript
复制
HttpRequest &HttpRequest::onResponse(const QObject *receiver, 
                                     const char *slot, 
                                     HttpResponse::SupportMethod type)
{
    m_slotsMap.insert(NUMBER_TO_STRING(type), {{slot, receiver}});
    return *this;
}

HttpRequest &HttpRequest::onError(const QObject *receiver, const char *slot)
{
    return onResponse(receiver, slot, HttpResponse::AutoInfer);
}

onResponse与onError函数

<2.2>比如接收者槽函数为void finish(QByteArray result), 则系统会自动QNetworkReplay的finish信号,当finish触发则返回QByteArray类型的结果给result变量。如果是接收者槽函数为void error(QString errorStr), 则系统后台自动绑定QNetworkReply的error信号,当返回错误状态码,系统会自动返回QString类型的结果给errorStr。

<2.3>目前支持自动识别的槽函数有:

代码语言:javascript
复制
void function(QNetworkReply* reply); Is_AutoInfer: true */
void function(QByteArray data); Is_AutoInfer: true */
void function(QVariantMap map); Is_AutoInfer: true */
void function(qint64 bytesReceived, qint64 bytesTotal); Is_AutoInfer: true */
void function(QNetworkReply::NetworkError error); Is_AutoInfer: true */
void function(QString errorString); Is_AutoInfer: true */
void function(QNetworkReply::NetworkError error, QNetworkReply* reply); Is_AutoInfer: true */
void function(QString errorString, QNetworkReply* reply); Is_AutoInfer: true */

3. exec魔法

(1)exec做的工作是发送Http请求,装载HttpResponse类;

(2)exec所使用的Http接口是createRequest,使用它是可以根据m_op属性选取对应的Http请求(get, post, put)。

代码语言:javascript
复制
HttpResponse *HttpRequest::exec()
{
    QNetworkReply* reply = NULL;
    QBuffer* sendBuffer = new QBuffer();
    QJsonObject sendJson = m_jsonBody;
    if (!sendJson.isEmpty()) {
        QByteArray sendByteArray = QJsonDocument(sendJson).toJson();
        sendBuffer->setData(sendByteArray);
    }

    reply = m_httpService->createRequest(m_op, m_networkRequest, sendBuffer);

    if (reply == NULL) {
        sendBuffer->deleteLater();
        return NULL;
    }
    else {
        sendBuffer->setParent(reply);
    }

    return new HttpResponse(reply, m_slotsMap);
}

exec函数

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Qt君 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档