前言
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版本来支持,如果没有对应的动态文件,编译时会如下报错:
以下提供一个传送门: