【Qt程式設計】基於Qt的詞典開發系列--使用者登入及API呼叫的實現
在上一篇文章《呼叫網路API》中,我只講述瞭如何直觀的使用API介面以及呼叫API後返回的結果,本文則從程式實現的角度來實現API的呼叫,當然本程式的實現也是藉助於扇貝網的API介面文件http://www.shanbay.com/help/developer/api/。
由API文件可知,要想呼叫其API,必須先註冊。因此,我就註冊了,賬戶名為nineheadedbird, 密碼為123456。顯然,我們要查詞,首先必須得登入該賬戶。如果用瀏覽器,那就很簡單,只需單純的輸入使用者名稱和密碼就可以了。可實際上,這一操作並不簡單,只是瀏覽器為我們做了這一切。如果我們要通過程式來實現上述功能的話,就需要用到Qt中的get()函數了,而傳送請求的內容格式就至關重要了。
檢視請求格式
我們可以通過瀏覽器來檢視請求格式:首先用谷歌瀏覽器(其他瀏覽器也可以,不過你要百度一下怎麼來檢視這些格式)開啟扇貝網的登入介面http://www.shanbay.com/accounts/login/ ,在谷歌瀏覽器的設定中單擊開發者選項,然後重新整理一下頁首,就會出現如下的介面:
然後點選右邊的第一個檔案login,就會出現下面的內容:
從上圖可以看出,內容分為三類:General、Response Headers、Request Headers
在General中可以看到Request Method為GET(一般還有另一種方式POST,這在Qt中都有對應的函式),Status Code為200表示正常。在Response Headers 中我們關注的是Set-Cookie中的csrftoken的值
我們的程式可以寫成如下的方式:
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request .setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
http->get(request);
當我們執行上述的請求之後,伺服器就會作答,作答的內容就是上面的Response Headers,而我們需要的是Set-Cookie中的csrftoken的值。在Qt中,我們將程式中finished訊號與我們定義的槽關聯,即每當網路應答結束時,都會發射這個訊號,從而觸發該槽函式的執行,來處理伺服器的應答內容。在程式中,getCookie函式就是來獲取csrftoken的值。
使用者登入
獲得csrftoken的值後,我們就需要實現登入操作了。除了上述的請求格式之外,我們還需要加入csrftoken的值、使用者名稱以及密碼。具體格式可見下述程式碼:
QNetworkRequest request; request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
request.setRawHeader("Origin","http//www.shanbay.com");
request.setRawHeader("Referer","http://www.shanbay.com/accounts/login/");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
QByteArray postData;
postData.append(QString("csrfmiddlewaretoken=%1&").arg(sessionid));//csrftoken的值
postData.append(QString("username=%1&password=%2&").arg(QUrl::toPercentEncoding(username).constData()).arg(password));//使用者名稱及密碼
postData.append("login=登入&continue=home&u=1&next=");
request.setHeader(QNetworkRequest::ContentLengthHeader,postData.size());
httpAction=LoginAction;
http->post(request,postData);
呼叫API
完成登入之後,就可以進行查詞和添詞操作了。除了上述提到的請求頭格式之外,只需要遵守API規範(《呼叫網路API》中提到請求格式)即可。查詞及添詞的程式實現分別如下:
void netWork::queryWord(const QString &word)//查詞操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/word/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordAction;
http->get(request);
}
void netWork::addWord(const QString &word)//添詞操作
{
if(word.isEmpty())
qDebug()<<"你的輸入有誤";
else
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/add/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=AddWordAction;
http->get(request);
}
}
完整流程
至此,API呼叫的各個功能已經實現,下面給出程式的整體思路:首先獲取csrftoken的值(每次都不同);然後利用使用者名稱、密碼及csrftoken的值來登入;接著就可以呼叫API了。在程式中,每當進行請求,都會在replyfinished函式中用case語句來分別處理這些請求對應的應答。注意,不要連續的進行請求,否則可能發生衝突。在程式中,為了防止衝突,我在connectNet請求後,在其應答處理函式中再進行loginShanbay的登入,然後在其應答函式中進行queryWord查詞請求,然後在其對應的應答處理函式中進行addWord添詞請求。其結果顯示如下:
程式實現
下面我們給出具體的程式實現(qt 5版本,使用到網路類,需要加上QT += network):首先建立一個空的qt子專案,然後新增一個名為netWork的類,繼承自QObject,然後再新增一個名為main的原始檔,這三個檔案的內容分別如下:
1、network.h檔案
#ifndef NETWORK_H
#define NETWORK_H
#include <QObject>
#include <QtNetwork/QNetworkAccessManager>
#include<QtNetwork/QNetworkReply>
#include<QtNetwork/QNetworkRequest>
#include<QtNetwork/QNetworkCookie>
#include<QtNetwork/QNetworkCookieJar>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include<QString>
#include<QDebug>
#include<QList>
#include<QUrl>
#include<QByteArray>
class netWork : public QObject //由於程式檔案直接摘自整個專案檔案,所以程式中有關的定義或函式沒有使用,但是這個程式可以單獨執行
{
Q_OBJECT
public:
explicit netWork(QObject *parent = 0);
// ~netWork();
enum HttpAction{NoAction,NetStudy,GetSessionidAction,LoginAction,QueryWordAction,AddWordAction,AddExampleAction,QueryWordExamplesAction};
HttpAction httpAction;
QNetworkAccessManager * http;
QString sessionid;
QString queryword;//要查詢的單詞
QString nickname;
QString username;
QString password;
bool isBusy;
QString getCookie(const QString &name);
void loginShanbay();
void queryWord(const QString &word);
void queryExamples(QString learningid);
void connectNet(QString username="nineheadedbird", QString password="123456");
void addWord(const QString &word);
signals://這裡的訊號都沒有用到
void connectSuccess();
void connectFail();
void verifySuccess();
void verifyFail();
void NetState(bool);
public slots:
void replyfinished(QNetworkReply*);
};
#endif // NETWORK_H
2、network.cpp檔案
#include "network.h"
#include<QList>
#include<QDesktopServices>
netWork::netWork(QObject *parent) :
QObject(parent)
{
http=new QNetworkAccessManager(this);
http->setCookieJar(new QNetworkCookieJar(this));
connect(http,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyfinished(QNetworkReply*)));//將finished訊號與我們定義的槽關聯,每當網路應答結束時,都會發射這個訊號
isBusy=true;
}
QString netWork::getCookie(const QString &name)//用於獲得SessionId
{
foreach(QNetworkCookie cookie , http->cookieJar()->cookiesForUrl(QUrl("http://www.shanbay.com/")))
{
if(cookie.name()==name)
{
qDebug()<<"csrftoken:"<<cookie.value();
return cookie.value();
}
}
return "";
}
void netWork::connectNet(QString username, QString password)//連線網路,使用預設的使用者名稱和密碼
{
this->username=username;
this->password=password;
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=GetSessionidAction;
http->get(request);
}
void netWork::replyfinished(QNetworkReply *reply)//每當執行網站應答結束後,就會執行該槽函式
{
QVariant status_code=reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qDebug()<<"code_state="<<status_code;//網路狀態,200代表正常,302代表重定向,404:not found等等
if(status_code==QVariant::Invalid)//判斷是否連線到網站,即當前裝置能否上網
emit NetState(false);
else
emit NetState(true);
switch(httpAction)//根據我們都進行了什麼網路請求
{
case NoAction:
break;
case GetSessionidAction://獲取SessionId
sessionid=getCookie("csrftoken");
if(!sessionid.isEmpty())
{
emit connectSuccess();
qDebug()<<("已經連線扇貝網,正在驗證使用者名稱密碼...");
loginShanbay();
}else
{
emit connectFail();
qDebug()<<("Cannot connect to the website!");
}
break;
case LoginAction: //進行登入操作
httpAction=NoAction;
if(0==reply->readAll().size())
{
QString nickname=QUrl::fromPercentEncoding(getCookie("username").toLatin1());
emit verifySuccess();
qDebug()<<"Successfully Login"<<nickname;
queryWord("hello");
}else
{
emit verifyFail();
qDebug()<<"Failed to login!";
}
break;
case QueryWordAction://查詞操作
qDebug()<<"----query word----";
qDebug()<<reply->readAll();//讀取查詞結果
addWord("hello");//新增單詞到單詞本
break;
case AddWordAction://添詞操作
qDebug()<<"---add word----";
qDebug()<<reply->readAll();//返回新增詞語的learning_id
break;
default:break;
}
}
void netWork::loginShanbay()//賬戶密碼的登入操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/accounts/login/"));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
request.setRawHeader("Origin","http//www.shanbay.com");
request.setRawHeader("Referer","http://www.shanbay.com/accounts/login/");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("Content-Type","application/x-www-form-urlencoded");
QByteArray postData;
postData.append(QString("csrfmiddlewaretoken=%1&").arg(sessionid));
postData.append(QString("username=%1&password=%2&").arg(QUrl::toPercentEncoding(username).constData()).arg(password));
postData.append("login=登入&continue=home&u=1&next=");
request.setHeader(QNetworkRequest::ContentLengthHeader,postData.size());
httpAction=LoginAction;
http->post(request,postData);
}
void netWork::queryWord(const QString &word)//查詞操作
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/word/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=QueryWordAction;
http->get(request);
}
void netWork::addWord(const QString &word)//添詞操作
{
if(word.isEmpty())
qDebug()<<"你的輸入有誤";
else
{
QNetworkRequest request;
request.setUrl(QUrl("http://www.shanbay.com/api/learning/add/"+word));
request.setRawHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
request.setRawHeader("Accept-Charset","GBK,utf-8;q=0.7,*;q=0.3");
request.setRawHeader("Accept-Language","zh-CN,zh;q=0.8");
request.setRawHeader("Cache-Control","max-age=0");
request.setRawHeader("Connection","keep-alive");
request.setRawHeader("Host","www.shanbay.com");
request.setRawHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7");
httpAction=AddWordAction;
http->get(request);
}
}
3、main.cpp檔案
#include <QApplication>
#include "network.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
netWork *nW = new netWork();
//
nW->connectNet();
// nW->loginShanbay();
// nW->queryWord("hello");
return a.exec();
}
作者:nineheadedbird