QtWebApp

wuyuan2011woaini發表於2024-03-13

1.下載

  https://github.com/fffaraz/QtWebApp

2.功能

  • HTTP(S) 1.0 and 1.1 server (web伺服器)
  • Template engine (模板)
  • Buffered logger(日誌)

3.使用

  1.web

  簡單例子:post上傳檔案到nginx的後臺資料處理

 1 #include "UploadFileNginx.h"
 2 #include<QSettings>
 3 UploadFileNginx::UploadFileNginx(QWidget *parent)
 4     : QMainWindow(parent)
 5 {
 6     ui.setupUi(this);
 7     OnInit();
 8 }0
 9 
10 UploadFileNginx::~UploadFileNginx()
11 {}
12 void UploadFileNginx::OnInit()
13 {
14     QString configFileName = "UploadFileNginx.ini";
15     QSettings* listenerSettings = new QSettings(configFileName, QSettings::IniFormat, this);
16     listenerSettings->beginGroup("listener");
17     AppDataHandler = new HttpRequestHandler(this);
18     AppDataListener = new HttpListener(listenerSettings, AppDataHandler, this);
19 }
 1 void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response)
 2 {
 3     if (request.getMethod() == "GET")//APP資料請求
 4     {
 5 
 6         //返回
 7         response.setStatus(200, "OK");
 8         response.setHeader("Content-Type", "application/json");
 9         response.write("", true);
10     }
11     else
12     {
13         QByteArray name = request.getParameter("file.name");
14         QByteArray path = request.getParameter("file.path");
15 
16 
17         int index = path.lastIndexOf("/");
18         if (index != -1)
19         {
20             QString fileName = path.left(index + 1) + name;
21             QFileInfo fileInfo(fileName);
22             if (fileInfo.isFile())
23             {
24                 QFile::remove(fileName);
25             }
26             if (!QFile::rename(path, fileName))
27             {
28                 //失敗
29             }
30         }
31 
32 
33         response.setStatus(200, "OK");
34         response.setHeader("Content-Type", "application/json");
35         response.write("", true);
36     }
37 }

  2.模板

  配置檔案webapp1.ini

1 [templates]
2 path=../docroot
3 suffix=.html
4 encoding=UTF-8
5 cacheSize=1000000
6 cacheTime=60000
  • path 還是相對於配置檔案的資料夾。這是我們用來儲存模板檔案(不完整的HTML檔案,包含可變內容的佔位符)的資料夾。
  • suffix 是字尾,將被新增到模板名稱後面。如果載入模板"wolfi",那麼模板引擎將會載入檔案 “wolfi.html”。你可以使用任何字尾,它對程式沒有任何意義。有些人喜歡使用“ .dhtml”來區分靜態和動態HTML檔案。因此,它是可配置的。
  • encoding 引數是必需的,因為模板檔案會被讀入QString物件裡。我更喜歡UTF-8,因為它支援任何語言的所有字元。我還使用具有UTF-8功能的文字編輯器來編輯它們(Linux下的gedit或Windows下的Notepad ++)。
  • 為了提高效能,伺服器快取了QString物件。 CacheSize 指定快取可能佔用的最大記憶體量。
  • cacheTime 指定檔案在記憶體中最多保留多長時間。我們已經為靜態檔案控制器設定了相同的設定。

  我們需要一個 TemplateCache 例項的全域性指標,使整個程式都可以訪問它。

  首先新增到global.h:

 1 #ifndef GLOBAL_H
 2 #define GLOBAL_H
 3 
 4 #include "httpsessionstore.h"
 5 #include "staticfilecontroller.h"
 6 #include "templatecache.h"
 7 
 8 using namespace stefanfrings;
 9 
10 extern HttpSessionStore* sessionStore;
11 extern StaticFileController* staticFileController;
12 extern TemplateCache* templateCache;
13 
14 #endif // GLOBAL_H

  global.cpp:

1 #include "global.h"
2 
3 HttpSessionStore* sessionStore;
4 StaticFileController* staticFileController;
5 TemplateCache* templateCache;

  在main.cpp中,我們配置了 TemplateCache:

 1 int main(int argc, char *argv[])
 2 {
 3     QCoreApplication app(argc, argv);
 4     QString configFileName=searchConfigFile();
 5 
 6     // Session store
 7     QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
 8     sessionSettings->beginGroup("sessions");
 9     sessionStore=new HttpSessionStore(sessionSettings,&app);
10 
11     // Static file controller
12     QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
13     fileSettings->beginGroup("files");
14     staticFileController=new StaticFileController(fileSettings,&app);
15 
16     // Configure template cache
17     QSettings* templateSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
18     templateSettings->beginGroup("templates");
19     templateCache=new TemplateCache(templateSettings,&app);
20 
21     // HTTP server
22     QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
23     listenerSettings->beginGroup("listener");
24     new HttpListener(listenerSettings,new RequestMapper(&app),&app);
25 
26     return app.exec();
27 }

  建立一個新類 DataTemplateController,它繼承自 HttpRequestHandler。我們將其繫結到路徑“/list2”。

  datatemplatecontroller.h:

 1 #ifndef DATATEMPLATECONTROLLER_H
 2 #define DATATEMPLATECONTROLLER_H
 3 
 4 #include "httprequesthandler.h"
 5 
 6 using namespace stefanfrings;
 7 
 8 class DataTemplateController: public HttpRequestHandler {
 9     Q_OBJECT
10 public:
11     DataTemplateController(QObject* parent=0);
12     void service(HttpRequest& request, HttpResponse& response);
13 private:
14     QList<QString> list;
15 };
16 
17 #endif // DATATEMPLATECONTROLLER_H

  datatemplatecontroller.cpp:

 1 #include "datatemplatecontroller.h"
 2 #include "template.h"
 3 #include "global.h"
 4 
 5 DataTemplateController::DataTemplateController(QObject* parent)
 6     : HttpRequestHandler(parent) {
 7     list.append("Robert");
 8     list.append("Lisa");
 9     list.append("Hannah");
10     list.append("Ludwig");
11     list.append("Miranda");
12     list.append("Fracesco");
13     list.append("Kim");
14     list.append("Jacko");
15 }
16 
17 void DataTemplateController::service(HttpRequest &request, HttpResponse &response) {
18     QByteArray language=request.getHeader("Accept-Language");
19     response.setHeader("Content-Type", "text/html; charset=UTF-8");
20 
21     Template t=templateCache->getTemplate("files/hello",language);
22     response.write(t.toUtf8(),true);
23 }

  requestmapper.h:

 1 #ifndef REQUESTMAPPER_H
 2 #define REQUESTMAPPER_H
 3 
 4 ...
 5 #include "datatemplatecontroller.h"
 6 
 7 using namespace stefanfrings;
 8 
 9 class RequestMapper : public HttpRequestHandler {
10     Q_OBJECT
11 public:
12     ...
13 private:
14     ...
15     DataTemplateController dataTemplateController;
16 };
17 
18 #endif // REQUESTMAPPER_H

  requestmapper.cpp:

 1 ...
 2 void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
 3     QByteArray path=request.getPath();
 4     ...
 5     else if (path=="/list2") {
 6         dataTemplateController.service(request, response);
 7     }
 8     else if (path.startsWith("/files")) {
 9         staticFileController->service(request,response);
10     }
11     ...
12 
13     qDebug("RequestMapper: finished request");
14 }

  修改模板

  • 變數

    html

 1 <html>
 2     <body>
 3         ...
 4         <p>
 5         List of names:
 6         <table>
 7             {loop row}
 8                 <tr>
 9                     <td>{row.number}</td>
10                     <td>{row.name}</td>
11                 </tr>
12             {end row}
13         </table>
14     </body>
15 </html>

    datatemplatecontroller.cpp

 1 void DataTemplateController::service(HttpRequest &request, HttpResponse &response) {
 2     HttpSession session=sessionStore->getSession(request,response,true);
 3     QString username=session.get("username").toString();
 4     QByteArray language=request.getHeader("Accept-Language");
 5     qDebug("language=%s",qPrintable(language));
 6     
 7     response.setHeader("Content-Type", "text/html; charset=UTF-8");
 8 
 9     Template t=templateCache->getTemplate("listdata",language);
10     t.setVariable("name",username.toString());
11     response.write(t.toUtf8(),true);
12 }

    佔位符 {name} 被替換為值 “test”

    

  • 條件語句

    html

 1 <html>
 2     <body>
 3         {if logged-in}
 4             Hello {name}
 5         {else logged-in}
 6             You are not logged in. 
 7             <br>
 8             Go to the <a href="/login">login</a> page and then try again.
 9         {end logged-in}
10     </body>
11 </html>

    datatemplatecontroller.cpp

 1 void DataTemplateController::service(HttpRequest &request, HttpResponse &response) {
 2     HttpSession session=sessionStore->getSession(request,response,true);
 3     QString username=session.get("username").toString();
 4     QByteArray language=request.getHeader("Accept-Language");
 5     qDebug("language=%s",qPrintable(language));
 6     
 7     response.setHeader("Content-Type", "text/html; charset=UTF-8");
 8 
 9     Template t=templateCache->getTemplate("listdata",language);
10     t.setVariable("name",username);
11     t.setCondition("logged-in",!username.isEmpty());
12     response.write(t.toUtf8(),true);
13 }

    設定條件"logged-in"判斷結果

  • 迴圈

    html

 1 <html>
 2     <body>
 3         ...
 4         <p>
 5         List of names:
 6         <table>
 7             {loop row}
 8                 <tr>
 9                     <td>{row.number}</td>
10                     <td>{row.name}</td>
11                 </tr>
12             {end row}
13         </table>
14     </body>
15 </html>

    datatemplatecontroller.cpp

 1 void DataTemplateController::service(HttpRequest &request, HttpResponse &response) {
 2     HttpSession session=sessionStore->getSession(request,response,false);
 3     QString username=session.get("username").toString();
 4     QByteArray language=request.getHeader("Accept-Language");
 5     qDebug("language=%s",qPrintable(language));
 6 
 7     response.setHeader("Content-Type", "text/html; charset=UTF-8");
 8 
 9     Template t=templateCache->getTemplate("listdata",language);
10     t.setVariable("name",username);
11     t.setCondition("logged-in",!username.isEmpty());
12     t.loop("row",list.size());
13     for(int i=0; i<list.size(); i++) {
14         QString number=QString::number(i);
15         QString name=list.at(i);
16         t.setVariable("row"+number+".number",number);
17         t.setVariable("row"+number+".name",name);
18     }
19     response.write(t.toUtf8(),true);
20 }

  2.日誌

  ini配置

1 [logging]
2 minLevel=2
3 bufferSize=100
4 fileName=../logs/webapp1.log
5 maxSize=1000000
6 maxBackups=2
7 timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
8 msgFormat={timestamp} {typeNr} {type} {thread} {msg}
9 ;type的取值有: 0=DEBUG, 1=WARNING, 2=CRITICAL, 3=FATAL, 4=INFO
  • 你可以透過設定 bufferSize = 0 來禁用緩衝。在這種情況下,只有在設定的 minLevel 及以上的版本才會被寫入日誌檔案。
  • 檔名相對於配置檔案所在的資料夾。
  • maxSize 引數限制日誌檔案的大小(以位元組為單位)。超過此限制時,記錄器將開始建立新檔案。
  • maxBackups 指定在磁碟上應保留多少箇舊日誌檔案。
  • timestampFormat 設定了時間戳的格式。
  • msgFormat 設定每個訊息的格式。以下欄位可用:
{timestamp} 建立日期和時間
{typeNr} 訊息的型別或級別,數字格式(0-4)
{type} 訊息格式的訊息型別或級別(DEBUG, WARNING, CRITICAL, FATAL, INFO)
{thread} 執行緒的ID號
{msg} 訊息文字
{xxx} 自定義的任何記錄器變數

QT 5.0和更高版本在除錯模式下還有一些其他變數:
{file} 生成訊息的原始碼檔名
{function} 生成訊息的功能
{line} 生成訊息的行號

  我們需要一個 FileLogger 例項的全域性指標,使整個程式都可以訪問它。首先新增到global.h:

 1 #include "httpsessionstore.h"
 2 #include "staticfilecontroller.h"
 3 #include "templatecache.h"
 4 #include "filelogger.h"
 5 
 6 using namespace stefanfrings;
 7 
 8 /** Storage for session cookies */
 9 extern HttpSessionStore* sessionStore;
10 
11 /** Controller for static files */
12 extern StaticFileController* staticFileController;
13 
14 /** Cache for template files */
15 extern TemplateCache* templateCache;
16 
17 /** Redirects log messages to a file */
18 extern FileLogger* logger;
19 
20 #endif // GLOBAL_H

  global.cpp:

1 #include "global.h"
2 
3 HttpSessionStore* sessionStore;
4 StaticFileController* staticFileController;
5 TemplateCache* templateCache;
6 FileLogger* logger;

  main.cpp

 1 int main(int argc, char *argv[])
 2 {
 3     QCoreApplication app(argc, argv);
 4     QString configFileName=searchConfigFile();
 5 
 6     // Configure logging
 7     QSettings* logSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
 8     logSettings->beginGroup("logging");
 9     logger=new FileLogger(logSettings,10000,&app);
10     logger->installMsgHandler();
11 
12     // Log the library version
13     qDebug("QtWebApp has version %s",getQtWebAppLibVersion());
14 
15     // Session store
16     QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
17     sessionSettings->beginGroup("sessions");
18     sessionStore=new HttpSessionStore(sessionSettings,&app);
19 
20     // Static file controller
21     QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
22     fileSettings->beginGroup("files");
23     staticFileController=new StaticFileController(fileSettings,&app);
24 
25     // Configure template cache
26     QSettings* templateSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
27     templateSettings->beginGroup("templates");
28     templateCache=new TemplateCache(templateSettings,&app);
29 
30     // HTTP server
31     QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
32     listenerSettings->beginGroup("listener");
33     new HttpListener(listenerSettings,new RequestMapper(&app),&app);
34 
35     return app.exec();
36 }