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 }