【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下载

相关推荐
晓纪同学4 小时前
QT-简单视觉框架代码
开发语言·qt
威桑4 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服4 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
fyzy4 小时前
Qt获取本地计算的CPU温度
qt
cbdg37574 小时前
Qt 6 QML Settings location 不创建指定路径文件
qt
了一li4 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
杨德杰4 小时前
QT网络(一):主机信息查询
网络·qt
黄金右肾5 小时前
Qt之串口设计-线程实现(十二)
qt·thread·serialport
飞飞-躺着更舒服6 小时前
【QT】实现电子飞行显示器(改进版)
开发语言·qt
CYBEREXP20087 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos