qt實現web服務器加載vue應用進行C++和html混合編程-連載【6】-企業級系統開發實戰連載系列 -技術棧(vue、element-ui、qt、c++、sqlite)


https://blog.csdn.net/m0_49654513/article/details/108353619

 

標題作者背景描述:
為什么寫此系列文章?
解決方案:
預覽Demo
應用程序的架構
下面是實操步驟:
添加全局靜態對象文件
global.h文件
main.cpp 添加靜態資源文件支持:
修改 requestmapper.cpp 文件
修改配置文件BitPos.ini
運行應用程序
編譯vue項目
標題作者背景描述:
本人就職於外資IT企業,擔任電商訂單處理產品開發經理一職,領導過非常多次大小項目的開發工作,對電商平台訂單處理流程非常熟悉。

公司專注鞋服行業相關軟件開發和服務,公司規模100多人以上,在台北,廣州,成都,上海,北京,國外等均有分公司。

為什么寫此系列文章?
本人在學校至工作到現在十余年時間,使用.net C# 開發語言,結合在公司實際開發,和市場的需求中,NET.開發的商業企業級系統遇到的缺點有如下:

程序首次加載慢,因為虛擬機編譯的原因。
WINFORM界面開發不夠炫麗,精美。
WINFORM界面設計人員難找。
程序可以被反編譯。
安裝包過大,部署麻煩,framework.
跨平台不夠好。
解決方案:
結合近年來前端設計的走向,最終選擇了qt+vue+element UI+sqlite(數據庫根據需要情況選擇)

qt負責接口和硬件處理

sqlite做數據存儲

vue+element UI 實現前端。

預覽Demo


應用程序的架構
我看先看看要實現BitPos應用程序的架構:

應用程序嵌套了google 瀏覽器內核,和http web 服務器.
google 瀏覽器內核實現了前端UI,
前端通過 http request 與后端C++代碼進行將互。
當然,代碼經過小調整,重新編譯,前后端也可以分開部署到任何類型的操作系統中。

下面是實操步驟:
打開上一節的項目源碼:bitpos
上一節的文章鏈接如下:
https://blog.csdn.net/m0_49654513/article/details/108184324

添加全局靜態對象文件
添加全局靜態對象文件 global.cpp
選中src 右鍵,添加新項:

 

global.h文件
如下:

#pragma once
#include <httpserver/staticfilecontroller.h>
using namespace stefanfrings;

/** Controller for static files */
extern StaticFileController* staticFileController;

1
2
3
4
5
6
7
global.cpp 文件如下:

#include "global.h"

StaticFileController* staticFileController;

1
2
3
4
main.cpp 添加靜態資源文件支持:
#include "src/global.h"
1
添加靜態資源文件支持,如下:


// Configure static file controller
QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&a);
fileSettings->beginGroup("docroot");
staticFileController=new StaticFileController(fileSettings,&a);

修改訪問地址如下:
view.setUrl(QUrl("http://localhost:5050"));

完整的main.cpp文件如下:
#include "BitPos.h"
#include <QtWidgets/QApplication>
#include <QWebEngineView>
#include <httpserver/httplistener.h>
#include <logging/filelogger.h>
#include <qdir.h>
#include "src/requestmapper.h"
#include "src/global.h"

using namespace stefanfrings;

/** Search the configuration file */
QString searchConfigFile()
{
QString binDir = QCoreApplication::applicationDirPath();
QString appName = QCoreApplication::applicationName();
QString fileName(appName + ".ini");

QStringList searchList;
searchList.append(binDir);
searchList.append(binDir + "/etc");
searchList.append(binDir + "/../etc");
searchList.append(binDir + "/../../etc"); // for development without shadow build
searchList.append(binDir + "/../" + appName + "/etc"); // for development with shadow build
searchList.append(binDir + "/../../" + appName + "/etc"); // for development with shadow build
searchList.append(binDir + "/../../../" + appName + "/etc"); // for development with shadow build
searchList.append(binDir + "/../../../../" + appName + "/etc"); // for development with shadow build
searchList.append(binDir + "/../../../../../" + appName + "/etc"); // for development with shadow build
searchList.append(QDir::rootPath() + "etc/opt");
searchList.append(QDir::rootPath() + "etc");

foreach(QString dir, searchList)
{
QFile file(dir + "/" + fileName);
if (file.exists())
{
// found
fileName = QDir(file.fileName()).canonicalPath();
qDebug("Using config file %s", qPrintable(fileName));
return fileName;
}
}

// not found
foreach(QString dir, searchList)
{
qWarning("%s/%s not found", qPrintable(dir), qPrintable(fileName));
}
qFatal("Cannot find config file %s", qPrintable(fileName));
}

int main(int argc, char* argv[])
{
QApplication a(argc, argv);
// Find the configuration file
QString configFileName = searchConfigFile();

// Configure static file controller
QSettings* fileSettings = new QSettings(configFileName, QSettings::IniFormat, &a);
fileSettings->beginGroup("docroot");
staticFileController = new StaticFileController(fileSettings, &a);

// Configure and start the TCP listener
QSettings* listenerSettings = new QSettings(configFileName, QSettings::IniFormat, &a);
listenerSettings->beginGroup("listener");
new HttpListener(listenerSettings, new RequestMapper(&a), &a);
//瀏覽器
QWebEngineView view;
//設置訪問地址
//view.setUrl(QUrl("http://localhost:5050/vue-element-admin/api/Login?user_code=333&password=3445"));
view.setUrl(QUrl("http://localhost:5050"));
//顯示瀏覽器窗口。
view.show();
return a.exec();
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
修改 requestmapper.cpp 文件
添加頭文件:

#include "global.h"
1
修改函數圈中的位置如下:


完整函數如下:

void RequestMapper::service(HttpRequest& request, HttpResponse& response)
{
QByteArray path=request.getPath().toLower();
qDebug("RequestMapper: path=%s",path.data());
fprintf(stderr, "request: %s\n", path.data());
//實現跨域訪問,js 調用API 提供了支持。
response.setHeader("Connection", "keep-alive");
auto origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,x-token");
response.setHeader("Access-Control-Max-Age", "86400");
response.setHeader("Vary", "Accept-Encoding,Origin");
response.setHeader("Keep-Alive", "timeout=2,max=99");
//set api header
response.setHeader("Content-Type", "application/json; charset=utf-8");
//response.setHeader("Access-Control-Allow-Origin", "*"); // also important , if not set , the html application wont run.
if (request.getMethod() == "OPTIONS")
{
response.setStatus(200,"OK");
qDebug("RequestMapper: finished request");
// Clear the log buffer

return;
}
else
{

}

// For the following pathes, each request gets its own new instance of the related controller.
QByteArrayList items = path.split('/');
QByteArray areaname;
QByteArray controlname;
QByteArray actionname;
QByteArray a, b, c;

for (int i = 0; i < items.length(); i++)
{
QByteArray first = items[i];
if (first.isEmpty())
continue;
else
{
//get control and action of name.
a = first;
if(i+1<items.length())
b = items[i + 1].toLower();

if (i + 2 < items.length())
c = items[i + 2].toLower();
break;
}
}
QList<QString> controls;
//判斷是否是路由。
if (Area.contains(a))
{
areaname = a;
controlname = b;
actionname = c;
controls=Area.values(a);
}
else
{
controlname = a;
actionname = b;
}

QString className = (controlname + "Controller").toLower();

int id = QMetaType::type(className.toLatin1());
HttpRequestHandler* result = NULL;
//判斷area
if (id != QMetaType::UnknownType)
{
if (controls.count() > 0 && !controls.contains(className))
{

staticFileController->service(request, response);
qDebug() << "訪問靜態文件!";
qDebug("RequestMapper: finished request");

return;
}
}

if (id != QMetaType::UnknownType)
{
result = static_cast<HttpRequestHandler*>(QMetaType::create(id));
const QMetaObject * theMetaObject = result->metaObject();
int nMetathodCount = theMetaObject->methodCount();
QByteArray method;
//查找方法
for (int nMetathodIndex = 0; nMetathodIndex < nMetathodCount; nMetathodIndex++)
{
QByteArray oneMethod = theMetaObject->method(nMetathodIndex).name();
if (actionname.compare(oneMethod, Qt::CaseSensitivity::CaseInsensitive)==0)
{
method = oneMethod;
break;
}
}
if (!method.isEmpty())
{
auto token=request.getHeader("X - Token");
//判斷token是否是可用。
auto v = QMetaObject::invokeMethod(result, method.data(), Qt::DirectConnection,
Q_ARG(HttpRequest &, request),
Q_ARG(HttpResponse &, response));
if (!v)
qDebug() << method.data()<<" method invokeMethod is error!";
}
else
{
//不存在的方法。
auto v = QMetaObject::invokeMethod(result, "ApiResult", Qt::DirectConnection,
Q_ARG(const QString&, actionname+" action not found !"),
Q_ARG(int, 1),
Q_ARG(HttpRequest &, request),
Q_ARG(HttpResponse &, response));
if (!v)
qDebug() << " service method invokeMethod is error!";
}
delete result;
}
else
{
staticFileController->service(request, response);
qDebug() << "訪問靜態文件!";
}

qDebug("RequestMapper: finished request");

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
修改配置文件BitPos.ini
[docroot]
path=docroot
encoding=UTF-8
maxAge=60000
cacheTime=60000
cacheSize=1000000
maxCachedFileSize=65536

1
2
3
4
5
6
7
8
靜態資源目錄為docroot
我們在etc目錄下添加一個index.html文件,測試顯示html5能力,內容如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>這是一個例子</title>
<script>

function drawOneStar(ctx, i) {
//i為五邊形的邊長
ctx.beginPath();
ctx.moveTo(0, i * Math.sin(Math.PI / 5));//B點
ctx.lineTo(i * 2 * Math.cos(Math.PI / 5), i * Math.sin(Math.PI / 5));//BE
ctx.lineTo(i * Math.cos(Math.PI / 5 * 2), (i * Math.sin(Math.PI / 5 * 2)) + i * Math.sin(Math.PI / 5));//EC
ctx.lineTo(i * Math.cos(Math.PI / 5), 0);//CA
ctx.lineTo(i + i * Math.cos(Math.PI / 5 * 2), (i * Math.sin(Math.PI / 5 * 2)) + i * Math.sin(Math.PI / 5));//AD
ctx.lineTo(0, i * Math.sin(Math.PI / 5));//DB
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
}
function drawStar() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.shadowBlur = 30;
ctx.shadowColor = "red";
ctx.translate(300, 600);
ctx.rotate(-Math.PI / 8);
drawOneStar(ctx, 50);
for (var i = 0; i < 50; i++) {
ctx.translate(70, -25);
ctx.rotate(-Math.PI / 8);
ctx.scale(0.95, 0.95);

drawOneStar(ctx, 50);
}

}

</script>
</head>
<body marginwidth="0px" marginheight="0px" onload="drawStar()">
<canvas id="canvas" width="700px" height="800px" style="background-color:yellow"></canvas>
</body>
</html>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
運行應用程序
在vs2019 按ctrl+f5 運行效果非常酷,如下:


編譯vue項目
我們把第一節打包出來的vuex項目放到docroot目錄下,重新啟動程序。要看第一節的教程,參考這里:
https://blog.csdn.net/m0_49654513/article/details/108045868
執行命令行cmd,切換到項目 vue01
輸入

cd C:\Users\Administrator\vue01
1

輸入編譯命令,編譯vue項目 ,如下:

npm run build dev
1

項目目錄下出現dist 目錄就是編譯好的應用,我們進入目錄,把訪目錄下的文檔復制到docroot目錄下,

復制后:

在vs2019按f5執行


效果動畫如下:
源碼下載:
https://download.csdn.net/download/m0_49654513/12801482
下一節,我們集成前端框架。

索取源碼,技術溝通,編譯報錯,請加QQ群561506606 加群無需驗證。
點擊鏈接加入群聊【企業級系統實戰-qt vue.j】:https://jq.qq.com/?_wv=1027&k=CCmkgYYu
————————————————
版權聲明:本文為CSDN博主「m0_49654513」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_49654513/article/details/108353619


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM