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 ", 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:UsersAdministratorvue01
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

原文地址:https://www.cnblogs.com/chinasoft/p/15010927.html