網頁很多時候是動態的,於是本篇文章目標實現一個簡答的動態頁面—頁靜態頁面互相跳轉,點選可以跳轉到子頁面。
上一篇的「Hello World」應用程式確實輸出了簡單的純文字。但網路的語言是HTML。因此,讓看看如何生成HTML。將輸出當前時間,並顯示列表物件中的一些資料。
與第一個HelloWorldController類似,建立另一個名為ListDataController的新類。
#ifndef LISTDATACONTROLLER_H
#define LISTDATACONTROLLER_H
#include <QList>
#include <QString>
#include "httprequesthandler.h"
using namespace stefanfrings;
class ListDataController: public HttpRequestHandler {
Q_OBJECT
public:
ListDataController(QObject* parent=0);
void service(HttpRequest& request, HttpResponse& response);
private:
QList<QString> list;
};
#endif // LISTDATACONTROLLER_H
#include <QTime>
#include "listdatacontroller.h"
ListDataController::ListDataController(QObject* parent)
: HttpRequestHandler(parent) {
list.append("Robert");
list.append("Lisa");
list.append("Hannah");
list.append("Ludwig");
list.append("Miranda");
list.append("Francesco");
list.append("Kim");
list.append("Jacko");
}
void ListDataController::service(HttpRequest &request, HttpResponse &response) {
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.write("<html><body>");
response.write("The time is ");
QString now=QTime::currentTime().toString("HH:mm:ss");
response.write(now.toUtf8());
response.write("<p>List of names:");
response.write("<table border='1' cellspacing='0'>");
for(int i=0; i<list.size(); i++) {
QString number=QString::number(i);
QString name=list.at(i);
response.write("<tr><td>");
response.write(number.toUtf8());
response.write("</td><td>");
response.write(name.toUtf8());
response.write("</td></tr>");
}
response.write("</table>");
response.write("</body></header>",true);
}
建構函式用一些名稱填充列表。該服務方法輸出一個具有當前時間的HTML檔案和一個顯示列表物件內容的表。
請注意,在編寫檔案之前設定了一個HTTP響應頭,它告訴瀏覽器使用的檔案格式(請參閱Internet Media Types)和字元編碼。
用新的控制器替換main.cpp中的控制器:
#include "listdatacontroller.h"
new HttpListener(listenerSettings,new ListDataController(&app),&app);
執行並測試應用程式。輸出應該是這樣的:
現在在應用程式中有兩個不同的控制器類,但一次只能使用一個。現在建立一個「RequestMapper」類,它將在兩個控制器之間切換。和以前一樣,新類再次從HttpRequestHandler繼承。
#ifndef REQUESTMAPPER_H
#define REQUESTMAPPER_H
#include "httprequesthandler.h"
using namespace stefanfrings;
class RequestMapper : public HttpRequestHandler {
Q_OBJECT
public:
RequestMapper(QObject* parent=0);
void service(HttpRequest& request, HttpResponse& response);
};
#endif // REQUESTMAPPER_H
#include "requestmapper.h"
#include "helloworldcontroller.h"
#include "listdatacontroller.h"
RequestMapper::RequestMapper(QObject* parent)
: HttpRequestHandler(parent) {
// empty
}
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
if (path=="/" || path=="/hello") {
HelloWorldController().service(request, response);
}
else if (path=="/list") {
ListDataController().service(request, response);
}
else {
response.setStatus(404,"Not found");
response.write("The URL is wrong, no such document.",true);
}
qDebug("RequestMapper: finished request");
}
用新的請求對映程式替換main.cpp中的舊控制器:
#include "requestmapper.h"
new HttpListener(listenerSettings,new RequestMapper(&app),&app);
請求對映器根據請求的路徑呼叫兩個控制器中的一個。所以
例如,如果試圖開啟任何錯誤的URLhttp://localhost:8080/lalala,然後會收到錯誤訊息「URL錯誤…」以及狀態程式碼404,這是「未找到」的技術值。一些程式使用它來處理錯誤。如果沒有設定狀態程式碼,那麼將使用預設的200,這意味著「成功」。請參閱維基百科中的HTTP狀態程式碼列表。
如果多個並行HTTP請求同時傳入,那麼service()方法將並行執行多次。所以這個方法是多執行緒的。當存取在service()方法外部宣告的變數時,必須考慮這一點。
請求對映器是「singleton」或處於「application scope」,因為它只有一個範例。
兩個控制器類(HelloWorldController和ListDataController)位於「請求範圍」中,這意味著每個請求都由該類的新範例處理。這會降低一些效能,但會稍微簡化程式設計。
一個小的修改將兩個控制器類的範圍更改為「應用程式範圍」。(PS:這個就是每次執行的時候,不是去新new)
#ifndef REQUESTMAPPER_H
#define REQUESTMAPPER_H
#include "httprequesthandler.h"
#include "helloworldcontroller.h"
#include "listdatacontroller.h"
using namespace stefanfrings;
class RequestMapper : public HttpRequestHandler {
Q_OBJECT
public:
RequestMapper(QObject* parent=0);
void service(HttpRequest& request, HttpResponse& response);
private:
HelloWorldController helloWorldController;
ListDataController listDataController;
};
#endif // REQUESTMAPPER_H
#include "requestmapper.h"
RequestMapper::RequestMapper(QObject* parent)
: HttpRequestHandler(parent) {
// empty
}
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
if (path=="/" || path=="/hello") {
helloWorldController.service(request, response);
}
else if (path=="/list") {
listDataController.service(request, response);
}
else {
response.setStatus(404,"Not found");
response.write("The URL is wrong, no such document.");
}
qDebug("RequestMapper: finished request");
}
現在,每個請求都只重新使用一個HelloWorldController或ListDataController範例。在啟動期間只建立一次,因為HttpRequestMapper也只存在一次。
使用對談後,還可以為每個對談建立控制器範例,並將它們儲存在對談儲存中。然後就有了一個「對談範圍」。對談將在後面進行解釋。
按照上面得,實際上就是剛開始出一個列表,/和/hello會出現helloworld,而/list則會返回list列表,這是直接通過url後得子網頁來切換(PS:並不是通過點選主頁面來切換,這個後面會解說到)。
其實這個對於做網頁得很簡單,就是一個點選超連線,只是跳轉到內部,使用<a>來實現的。
準備之前的demo模板:
copy原來的helloworld改成index(PS:不管又不有index,直接網站也是跳入index.html頁面)。
修改完類相關資訊。
// 啟動http的監聽
{
if(!_pHttpListenerSettings)
{
_pHttpListenerSettings = new QSettings(httpServerPath, QSettings::IniFormat);
}
_pHttpListenerSettings->beginGroup("listener");
// _pHttpListener = new HttpListener(_pHttpListenerSettings, new HelloWorldRequestHandler);
_pHttpListener = new HttpListener(_pHttpListenerSettings, new IndexRequestHandler);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>長沙紅胖子Qt</title>
</head>
<body>
<p>好, 長沙紅胖子 QQ:21497936 www.hpzwl.com"</p>
<p><a href="helloworld">Hello world!</a></p>
<p><a href="list">list</a></p>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>長沙紅胖子Qt</title>
</head>
<body>
<a href="javascript:history.back(-1);">返回上一頁</a>
<table border="2">
<tr>
<td>row 1, cell 1</td>
<td>row 1, cell 2</td>
</tr>
<tr>
<td>row 2, cell 1</td>
<td>row 2, cell 2</td>
</tr>
</table>
</body>
這裡有個亂碼問題,請檢視「入坑一」:
void IndexRequestHandler::service(HttpRequest &request, HttpResponse &response)
{
QString str;
QString path = request.getPath();
LOG << path;
if(path == "/" || path == "/index")
{
str += "<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>長沙紅胖子Qt</title>"
"</head>"
"<body>"
" <p>好, 長沙紅胖子 QQ:21497936 www.hpzwl.com</p>"
" <p><a href=\"helloworld\">Hello world!</a></p>"
" <p><a href=\"list\">list</a></p>"
"</body>";
}else if(path == "/helloworld")
{
helloWorldRequestHandler.service(request, response);
return;
}else if(path == "/list")
{
listRequestHandler.service(request, response);
return;
}else {
response.setStatus(404,"Not found");
str = "The URL is wrong, no such document.";
}
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// QByteArray byteArray = _pTextCodec->fromUnicode(str);
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
}
修改好宏類名,然後嵌入list.html頁面:
void ListRequestHandler::service(HttpRequest &request, HttpResponse &response)
{
QString str;
LOG << request.getPath();
str = "<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>長沙紅胖子Qt</title>"
"</head>"
"<body>"
"<a href=\"javascript:history.back(-1);\">返回上一頁</a>"
"<table border=\"2\">"
" <tr>"
" <td>row 1, cell 1</td>"
" <td>row 1, cell 2</td>"
" </tr>"
" <tr>"
" <td>row 2, cell 1</td>"
" <td>row 2, cell 2</td>"
" </tr>"
" </table>"
"</body>";
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// QByteArray byteArray = _pTextCodec->fromUnicode(str);
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
}
#ifndef INDEXREQUESTHANDLER_H
#define INDEXREQUESTHANDLER_H
#include "httprequesthandler.h"
#include "HelloWorldRequestHandler.h"
#include "ListRequestHandler.h"
using namespace stefanfrings;
class IndexRequestHandler : public HttpRequestHandler
{
public:
IndexRequestHandler(QObject *parent = 0);
public:
void service(HttpRequest& request, HttpResponse& response);
private:
QTextCodec *_pTextCodec;
private:
HelloWorldRequestHandler helloWorldRequestHandler; // hellowold訊息處理
ListRequestHandler listRequestHandler; // list訊息處理
};
#endif // INDEXREQUESTHANDLER_H
#include "IndexRequestHandler.h"
#include "ListRequestHandler.h"
#include <QTextCodec>
#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")
using namespace stefanfrings;
IndexRequestHandler::IndexRequestHandler(QObject *parent)
: HttpRequestHandler(parent)
{
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// WINDOWS: GBK GB2312
// LINUX : urf-8
// _pTextCodec = QTextCodec::codecForName("utf-8");
_pTextCodec = QTextCodec::codecForName("GBK");
}
void IndexRequestHandler::service(HttpRequest &request, HttpResponse &response)
{
QString str;
QString path = request.getPath();
LOG << path;
if(path == "/" || path == "/index")
{
str += "<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>長沙紅胖子Qt</title>"
"</head>"
"<body>"
" <p>好, 長沙紅胖子 QQ:21497936 www.hpzwl.com</p>"
" <p><a href=\"helloworld\">Hello world!</a></p>"
" <p><a href=\"list\">list</a></p>"
"</body>";
}else if(path == "/helloworld")
{
helloWorldRequestHandler.service(request, response);
return;
}else if(path == "/list")
{
listRequestHandler.service(request, response);
return;
}else {
response.setStatus(404,"Not found");
str = "The URL is wrong, no such document.";
}
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// QByteArray byteArray = _pTextCodec->fromUnicode(str);
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
}
#ifndef LISTREQUESTHANDLER_H
#define LISTREQUESTHANDLER_H
#include "httprequesthandler.h"
using namespace stefanfrings;
class ListRequestHandler : public HttpRequestHandler
{
public:
ListRequestHandler(QObject *parent = 0);
public:
void service(HttpRequest& request, HttpResponse& response);
private:
QTextCodec *_pTextCodec;
};
#endif // LISTREQUESTHANDLER_H
#include "ListRequestHandler.h"
#include <QTextCodec>
#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")
using namespace stefanfrings;
ListRequestHandler::ListRequestHandler(QObject *parent)
: HttpRequestHandler(parent)
{
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// WINDOWS: GBK GB2312
// LINUX : urf-8
// _pTextCodec = QTextCodec::codecForName("utf-8");
_pTextCodec = QTextCodec::codecForName("GBK");
}
void ListRequestHandler::service(HttpRequest &request, HttpResponse &response)
{
QString str;
LOG << request.getPath();
str = "<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>長沙紅胖子Qt</title>"
"</head>"
"<body>"
"<a href=\"javascript:history.back(-1);\">返回上一頁</a>"
"<table border=\"2\">"
" <tr>"
" <td>row 1, cell 1</td>"
" <td>row 1, cell 2</td>"
" </tr>"
" <tr>"
" <td>row 2, cell 1</td>"
" <td>row 2, cell 2</td>"
" </tr>"
" </table>"
"</body>";
// 返回文字(需要在瀏覽器上看,所以將Qt內部編碼都轉成GBK輸出即可,不管他本身是哪個編碼)
// QByteArray byteArray = _pTextCodec->fromUnicode(str);
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
}
亂碼
之前的網頁沒有編碼,直接轉換的,新建的頁面有編碼,標識了utf-8,所以無需轉碼gbk了。
當表示好頁面的編碼未utf-8之後,則無需字元轉換編碼。