1. 程式人生 > >【Qt程式設計】基於Qt的詞典開發系列--使用者登入及API呼叫的實現

【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的值

,因為這在我們登入時需要這個值。我們最關心的是Request Headers的內容,這部分就是我們請求函式中內容格式!參考上述的具體內容如下:
這裡寫圖片描述

我們的程式可以寫成如下的方式:

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