【QT常用技术讲解】发送POST包(两种方式:阻塞方式及非阻塞方式)

前言

http/https(应用层)协议是广泛使用的网络通信协议。在很多与第三方API对接的场景中,通常是通过http/https协议完成,比如API对接时,通常要通过POST包获取access_token进行鉴权,然后再进行数据交互(本篇也包含有对接收数据的json数据解析代码)。

本篇以百度AI的API接口的access_token鉴权为例,通过QT特性QNetworkAccessManager实现两种方式的POST包方式:阻塞方式和非阻塞方式。

功能讲解

阻塞方式

QT开发的工具,通常没有高并发的网络通信服务需求,类似原生socket一样,send完数据之后就(阻塞方式)recv数据,这是非常普遍的调用方式,以下展示调用百度AI的API鉴权接口的过程。

//头文件networkmanager.h
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QDebug>
#define API_KEY "GU1GeuGXNDhG48Z2aMYE2rD1"
#define SECRET_KEY "NxrnIMZ7hi8vfIT734SiLhmwCpStEN8K"

class NetworkManager : public QObject
{
    Q_OBJECT
public:
    explicit NetworkManager(QObject *parent = nullptr);
    QByteArray  block_post_data(const QNetworkRequest& request,const QUrlQuery& postData);
    QString get_access_token(const QString& client_id,const QString& client_secret) ;
};


//cpp文件networkmanager.cpp
#include "networkmanager.h"

NetworkManager::NetworkManager(QObject *parent) : QObject(parent)
{

}

QByteArray NetworkManager::block_post_data(const QNetworkRequest& request,const QUrlQuery& postData){
    //QString resstr;
    QByteArray responseData;
    // 创建网络访问管理器
    QNetworkAccessManager block_manager;//这里不要用QNetworkAccessManager *block_manager的方式
    // 发送POST请求
    QNetworkReply* reply = block_manager.post(request, postData.toString(QUrl::FullyEncoded).toUtf8());

    // 创建一个事件循环来阻塞当前线程
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    // 阻塞当前线程直到网络请求完成
    loop.exec();
    // 检查请求结果
    if (reply->error() == QNetworkReply::NoError) {
        // 请求成功,处理返回的数据
        /*QByteArray */responseData = reply->readAll();
        //resstr = QString::fromUtf8(responseData);
        //qDebug() << __LINE__ <<"Response:" << resstr;
    } else {
        // 请求失败,处理错误
        qDebug() << "Error:" << reply->errorString();
        responseData="";
    }
    // 清理
    reply->deleteLater();
    return responseData;
}


QString NetworkManager::get_access_token(const QString& client_id,const QString& client_secret) {
    QString access_token;
    QUrl url("https://aip.baidubce.com/oauth/2.0/token");
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    request.setRawHeader("Accept", "application/json");

    // 创建 POST 数据
    QUrlQuery postData;
    postData.addQueryItem("grant_type", "client_credentials");
    postData.addQueryItem("client_id", client_id);
    postData.addQueryItem("client_secret", client_secret);

    //发送Post包(通过阻塞方式获取返回结果)
    QByteArray jsonData=block_post_data(request,postData);
    // 解析 JSON 数据
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
    if (jsonDoc.isNull()) {
        qDebug() << "Failed to create JSON doc.";
        return "";
    }
    // 检查 JSON 文档类型
    if (jsonDoc.isObject()) {
        QJsonObject jsonObj = jsonDoc.object();
        //qDebug() << "JSON Object:" << jsonObj;
        // 访问 JSON 对象中的数据
        if (jsonObj.contains("access_token")) {
            access_token = jsonObj["access_token"].toString();
            qDebug() << "access_token:" << access_token;
        }
    } else {
        qDebug() << "JSON is not an object.";
    }
    return access_token;
}


//主函数文件main.cpp
#include "networkmanager.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    NetworkManager networkManager;
    QString access_token=networkManager.get_access_token(API_KEY,SECRET_KEY);
    return a.exec();
}

以上代码中,调用QNetworkAccessManager的post之后,通过循环事件来阻塞线程,直到获取到结果(QNetworkReply::finished)之后,才退出事件循环(QEventLoop::quit)。

    // 创建一个事件循环来阻塞当前线程
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    // 阻塞当前线程直到网络请求完成
    loop.exec();

除此之外,源代码还包含了json解析部分,因为只有一个层级,直接获取access_token字段的内容即可。

// 解析 JSON 数据
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
    if (jsonDoc.isNull()) {
        qDebug() << "Failed to create JSON doc.";
        return "";
    }
    // 检查 JSON 文档类型
    if (jsonDoc.isObject()) {
        QJsonObject jsonObj = jsonDoc.object();
        //qDebug() << "JSON Object:" << jsonObj;
        // 访问 JSON 对象中的数据
        if (jsonObj.contains("access_token")) {//-----json格式中最外层的access_token字段
            access_token = jsonObj["access_token"].toString();
            qDebug() << "access_token:" << access_token;
        }
    } else {
        qDebug() << "JSON is not an object.";
    }

非阻塞方式

在设计时,如果异步处理更加符合框架的处理性能需求时,比如鉴权完成之后,批量调用百度AI的OCR接口时,批量的组装并发送POST包,然后异步的接收数据之后,保存到本地(或者是抽取关键数据)的场景时,就得用异步的方式,以下只是展示调用百度AI的API鉴权接口来贴源代码。

//头文件networkmanager.h
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QByteArray>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QDebug>

#define API_KEY "GU1GeuGXNDhG48Z2aMYE2rD1"
#define SECRET_KEY "NxrnIMZ7hi8vfIT734SiLhmwCpStEN8K"

class NetworkManager : public QObject
{
    Q_OBJECT
public:
    explicit NetworkManager(QObject *parent = nullptr);
    void get_access_token(const QString& client_id,const QString& client_secret) ;
private slots:
    void onFinished(QNetworkReply *reply);

private:
    QNetworkAccessManager *manager;
};

--------------------------------------------------------------------------

//cpp文件networkmanager.cpp
#include "networkmanager.h"

NetworkManager::NetworkManager(QObject *parent) : QObject(parent)
{
    manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished, this, &NetworkManager::onFinished);//接收数据的槽函数
}


void NetworkManager::get_access_token(const QString& client_id,const QString& client_secret) {
    QUrl url("https://aip.baidubce.com/oauth/2.0/token");
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
    request.setRawHeader("Accept", "application/json");

    // 创建 POST 数据
    QUrlQuery postData;
    postData.addQueryItem("grant_type", "client_credentials");
    postData.addQueryItem("client_id", client_id);
    postData.addQueryItem("client_secret", client_secret);

    // 发送 POST 请求
    //manager->post(request, jsonData);
    // 发送 POST 请求
    manager->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());

}


void NetworkManager::onFinished(QNetworkReply *reply) {
    if (reply->error() == QNetworkReply::NoError) {
        QByteArray responseData = reply->readAll();
        qDebug() << "Response:" << responseData;
    } else {
        qDebug() << "Error:" << reply->errorString();
    }
    reply->deleteLater();
}


//主函数main.cpp
#include <QApplication>
#include "networkmanager.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    NetworkManager networkManager;
    networkManager.get_access_token(API_KEY,SECRET_KEY);

    return a.exec();
}

源码中通过信号和槽机制,统一获取并处理Post的返回结果。

//通过信号和槽机制,统一获取并处理Post的返回结果
manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &NetworkManager::onFinished);

void NetworkManager::onFinished(QNetworkReply *reply) {
    if (reply->error() == QNetworkReply::NoError) {
        QByteArray responseData = reply->readAll();
        qDebug() << "Response:" << responseData;
    } else {
        qDebug() << "Error:" << reply->errorString();
    }
    reply->deleteLater();
}

片尾

因为百度API是https调用的,QT环境需要专门匹配的SSL版本来支持,如果没有对应的动态文件,编译时会如下报错:

以下提供一个传送门:

解决qt.network.ssl 和 OpenSSL 1.1.1g Win64版本EXE下载

相关推荐
练小杰10 分钟前
Linux系统 C/C++编程基础——基于Qt的图形用户界面编程
linux·c语言·c++·经验分享·qt·学习·编辑器
勤又氪猿10 分钟前
【问题】Qt c++ 界面 lineEdit、comboBox、tableWidget.... SIGSEGV错误
开发语言·c++·qt
人才程序员2 小时前
【C++拓展】vs2022使用SQlite3
c语言·开发语言·数据库·c++·qt·ui·sqlite
追Star仙8 小时前
基于Qt中的QAxObject实现指定表格合并数据进行word表格的合并
开发语言·笔记·qt·word
Trouvaille ~14 小时前
PyQt5 超详细入门级教程上篇
开发语言·qt
深蓝海拓14 小时前
Pyside6(PyQT5)中的QTableView与QSqlQueryModel、QSqlTableModel的联合使用
数据库·python·qt·pyqt
北顾南栀倾寒1 天前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
Chris·Bosh1 天前
QT:控件属性及常用控件(3)-----输入类控件(正则表达式)
qt·正则表达式·命令模式
计算机内卷的N天1 天前
UI样式表(悬停hover状态样式和按下pressed)
qt
JANG10241 天前
【Qt】窗口
开发语言·qt