前言
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下载