上一篇文章介绍了在Qt中与网络请求相关的类,以及在Qt中生成和解析Json。这一篇文章,将基于实例展示如何应用这些类实现网络请求,以及Json的生成和解析体现在哪里。
先回顾一下需要解决的问题:
问题:用户联网登录
1.提供登录界面(客户端),输入用户名和密码;
2.将用户名和密码发送给服务器端,并进行相关查询,如果用户合法,返回用户信息;
3.对返回的用户信息进行解析。- 假设:
已经有了服务器和存储着用户名和密码数据库。服务器当接收到客服端的请求的时候,先在数据库中进行查询操作,然后返回用户信息。
其中,客户端与服务器进行通信用到的接口信息如下所示:
【url】 /xxx1/xxx2/
【请求方式】 post
【请求参数说明】
{“username”:6到15个字符, 数字和字母的组合,非空,唯一
“passwd”:6到20个字符,非空}
【样例说明】
{“passwd”:”123456”,”username”:”haha”}
【返回信息】
{“code”:84,”message”:”登陆失败,验证码输入错误”}
{“code”:4,”message”:”账号异常”}
{“code”:5,”message”:”密码错误”}
{“code”:6,”message”:”用户名不存在”}
一、客户端界面
二、实现步骤
1.生成QNetworkRequest对象,设置请求头、请求所用API所需实参、url等信息;
在这里根据请求方式是post还是get,请求所用API所需实参的设置分两种情况:
①manager.post(request,data)
其中data为形如
"data={"username": "haha","passwd": 123456}"
这样的QByteArray。
②manager.get(request)
参数直接在设置request的URL的时候设置(post要另设置URL):
request.setUrl(QUrl("http://xxx1/xxx2?username="+username+"&passwd="+passwd));
2.准备解析处理QNetworkReply的slot;
3.生成QNetworkAccessManager,发送request,并将返回来的reply委托给该slot处理(实现上就是一条connect语句)
三、代码实现
1.在.pro中添加netwotk模块
因为与网络请求相关的那些类在这个模块里面
QT += core gui //原来存在的
QT += network //添加的
2.login.h文件
#ifndef LOGIN_H
#define LOGIN_H
#include <QMainWindow>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
namespace Ui {
class Login;
}
class Login : public QMainWindow
{
Q_OBJECT
public:
explicit Login(QWidget *parent = 0);
~Login();
private slots:
void finishRequest(QNetworkReply*); //处理reply的slot
void on_OKButton_clicked();
private:
Ui::Login *ui;
QNetworkAccessManager manager; //manager 相关注意点见后文
QNetworkRequest request; //request
};
#endif // LOGIN_H
3.login.cpp
#include "login.h"
#include "ui_login.h"
#include <QMessageBox>
#include <QByteArray>
#include <QString>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonParseError>
#include <QJsonValue>
#include <QJsonObject>
#include <QDebug>
#include <iostream>
Login::Login(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Login)
{
ui->setupUi(this);
}
Login::~Login()
{
delete ui;
}
void Login::on_OKButton_clicked()
{
//获取用户名、密码
QString username = ui->usrLineEdit->text();
QString password = ui->pwdLineEdit->text();
if(username.isEmpty() || password.isEmpty())
{
QMessageBox::warning(this,"Warning","Username or password is empty!",QMessageBox::Yes);
ui->usrLineEdit->clear();
ui->pwdLineEdit->clear();
}
else{
//通过QJsonObject得到post需要的参数data, data形式为: "data={"username": "haha","passwd": 123456}"
QJsonObject json;
json.insert("username",username);
json.insert("passwd",password);
QJsonDocument document;
document.setObject(json);
QByteArray byte_array = document.toJson(QJsonDocument::Compact);
QByteArray data;
data.append("data="+byte_array);
//设置request
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); //上面语句固定这么写,要不然会报错“contest—type is missing”
request.setRawHeader("XXX3", "XXX4");
request.setUrl(QUrl("http://xxx1/xxx2/")); //"http:"不要少
//manager发送请求,并接收reply,委托给slot处理
manager.post(request,data);
connect(&manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishRequest(QNetworkReply*)));
}
}
void Login::finishRequest(QNetworkReply *reply)
{
if (reply->error()){
qDebug()<<"Error!"<<endl;
}else{
//解析reply中携带的Json
QByteArray byte_array = reply->readAll(); //Json ducument
QJsonParseError json_error;
QJsonDocument parse_document = QJsonDocument::fromJson(byte_array, &json_error);
if(json_error.error == QJsonParseError::NoError)
{
if(parse_document.isObject())
{
QJsonObject obj = parse_document.object();
if(obj.contains("code"))
{
QJsonValue code_value = obj.take("code");
int code_value_int = code_value.toInt();
if(code_value_int == 3)
{
QJsonValue message_value = obj.take("message");
qDebug()<<message_value<<endl; //输出:QJsonValue(string, "登录成功")
}
else
{
QJsonValue message_value = obj.take("message");
qDebug()<< message_value<<endl;
QString error_message = message_value.toString();
QMessageBox::warning(this,"Error",error_message,QMessageBox::Yes);
ui->usrLineEdit->clear();
ui->pwdLineEdit->clear();
}
}
}
}
}
reply->deleteLater();
}
四、实现时遇到的问题
1.试图直接生成Json document
post(request,data)请求中所用到的data为形如
"data={"username": "haha","passwd": 123456}"
的Json document,由于是一QByteArray的形式保存的,所以就想着能不能直接生成这样的QByteArray,从而绕过QJsonObject、 QJsonDocument, 实验结果是不可以。
//想通过QByteArray直接生成Json document,结果是不可以
QByteArray data;
data.append("username="+username);
data.append("passwd="+password);
2.将manager 和 request设为了全局变量
在这个例子中,只有一次网络请求,因此看不出这样做有什么问题。但是,当程序中有好几个网络请求的时候,这些网络请求共用一套manager和request,就会出现问题了。因为connect语句,只要manager接收到reply,就会触发所有connect语句,所有的解析Json的slot都有可能被委托到。这样就会出现,为什么我发出网络请求的是第二部分,却会是第一部分的slot有所反应= =
如果一开始使用的是全局变量,简单地将用到全局变量manager 和 request的地方改为局部变量
QNetworkAccessManager manager1;
QNetworkRequest request1;
出现的结果是,根本就没有网络请求成功的迹象。应该使用指针:
QNetworkAccessManager *manager1 = new QNetworkAccessManager();
QNetworkRequest *request1= new QNetworkRequest();
原因参看:http://bbs.csdn.net/topics/391077810
但如果并不是由全局变量改为局部变量,采用第一种方式好像不会有什么问题。
参考资料:
网络请求基础相关:
Qt中网络请求相关类相关:
Qt实现网络数据传输: http://blog.sina.com.cn/s/blog_a6fb6cc90101fg0i.html
Post与get的区别: http://www.cnblogs.com/findumars/p/5574299.html
Qt网络编程之实例一GET方式: http://blog.csdn.net/chenlong12580/article/details/7392766
解决finished信号无法触发槽函数是看到的:
QNetworkAccessManager的finished信号无法触发槽函数: http://bbs.csdn.net/topics/391077810
QNetworkAccessManager : https://tieba.baidu.com/p/4089426961
Json相关:
Qt之JSON生成与解析: http://blog.sina.com.cn/s/blog_a6fb6cc90101gnxm.html
Json教程:http://www.w3school.com.cn/json/index.asp
http://www.w3school.com.cn/json/json_syntax.asp