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

相关推荐
用户805533698034 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner5 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能12 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G12 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt