qt 实现MVC Api控制器开发 web api接口-连载【5】-企业级系统开发实战连载系列 -技术栈vue、element-ui、qt、c++、sqlite

  • 标题作者背景描述:
    • 为什么写此系列文章?
    • 解决方案:
    • 预览Demo
  • MVC 模型
  • 下载QtWebApp
  • 解压QtWebApp
  • 添加到解决方案
  • 设置静态链接
  • 设置QT版本
  • 编译源码
  • 引用QtwebApp
  • 添加包含目录
  • 修改BitPos静态链接
  • 实现mvc
  • 添加API 控制器
  • 添加controller类
  • 实现模型Login 动作
  • 添加控制器处理入口
  • 使用webengine实现 view
  • 编译并调试View
  • 总结

标题作者背景描述:

本人就职于外资IT企业,担任电商订单处理产品开发经理一职,领导过非常多次大小项目的开发工作,对电商平台订单处理流程非常熟悉。

公司专注鞋服行业相关软件开发和服务,公司规模100多人以上,在台北,广州,成都,上海,北京,国外等均有分公司。

为什么写此系列文章?

本人在学校至工作到现在十余年时间,使用.net C# 开发语言,结合在公司实际开发,和市场的需求中,NET.开发的商业企业级系统遇到的缺点有如下:

  1. 程序首次加载慢,因为虚拟机编译的原因。
  2. WINFORM界面开发不够炫丽,精美。
  3. WINFORM界面设计人员难找。
  4. 程序可以被反编译。
  5. 安装包过大,部署麻烦,framework.
  6. 跨平台不够好。

解决方案:

结合近年来前端设计的走向,最终选择了qt+vue+element UI+sqlite(数据库根据需要情况选择)

qt负责接口和硬件处理

sqlite做数据存储

vue+element UI 实现前端。

预览Demo

MVC 模型


MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

Model(模型) - 模型代表一个存取数据的对象。它也可以带有逻辑,在数据变化时更新控制器。
View(视图) - 视图代表模型包含的数据的可视化。
Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

下载QtWebApp

描述:要实现web Api需要使用一个qt web app 框架。
下载QtWebApp,集成到项目中,这个库主要是实现http 协议,和 QT Web Api库支持。
下载方式 : https://download.csdn.net/download/m0_49654513/12741112

作用:对于单机版本,不需要用户安装iis 或 tomcat,简化了部署的难度。
QtWebApp目录结构如下:

解压QtWebApp

添加到解决方案

打开上一章节创建的解决方案:BitPos 。
在解决方案资源管理器,右键》添加》现有项目 ,如下:

把qtwebapp.vcxproj 添加现在项目 。

设置静态链接

选择QtWebApp ,右键》属性

在常规选项中修改sdk 版本,和平台工具集,配置类型,输出为静态库,如下:

设置QT版本

在qt project settings 选项中,修改 qt installation 为 我们上一节配置的qt版本,如下:

编译源码

编译源码,这里能够一次编译成功,如下图:

引用QtwebApp

点击BitPos项目,右键》添加》引用

添加包含目录

为BitPos添加 包含目录,因为后面会用QtWebApp的头文件。
选择BitPos 》属性》vc++目录》包含目录》输入QtWebApp 如下图:

修改BitPos静态链接

修改BitPos运行库为静态链接。
按上面的步骤,修改这2个项目的配置为release 模式,重复一次操作。否则release编译会报错。

实现mvc

接下来演示如何添加一个用户登入时密码验证的接口。
这个接口有2个参数,分别是user_code ,password
返回为json ,返回了用户代码和用户名称 ,分别是user_code,user_name 。
使用postman请求工具,测试和调试API,
请求描述如下图:

添加API 控制器

接下来进行实操:添加API 控制器 和登入接口Login的方法
新增 src 目录,然后在目录下创建 controller
如下图:

添加controller类

选中controller 右键》添加》Add qt class
输入类名:ApiController
如下图:

点add 如下图:

点击next:如下图
把Base class 修改为HttpRequestHandler

实现模型Login 动作

ApiController.h添加Login 函数,
如下:

 //action 登入接口。http://localhost:5050/vue-element-admin/api/loginQ_INVOKABLE  void Login(HttpRequest & request, HttpResponse & response);

在ApiController.cpp添加Login 实现,实现了用户和密码的简单验证,并返回用户结果,如下:

Q_INVOKABLE void ApiController::Login(HttpRequest & request, HttpResponse & response)
{//request.//获取post请求的表单 。QMultiMap<QByteArray, QByteArray> forms = request.getParameterMap();//获取用户提交表单中的user_code auto usercode = forms.value("user_code").trimmed();if (usercode.isEmpty()){Result("用户代码不能为空!", 1, response);return;}//获取用户提交表单中的password auto password = forms.value("password").trimmed();if (password.isEmpty()){Result("密码不能为空!", 1, response);return;}//二次判断QByteArray hash = QCryptographicHash::hash(password, QCryptographicHash::Algorithm::Sha256).toBase64();//验证用户代码和密码。QJsonObject object{{"code", 0},{"msg", QJsonValue::Null}};QJsonObject user{{"user_code", "admin"},{"user_name", "admin"}};object.insert("user", user);QJsonDocument usermodel(object);//返回用户信息。QByteArray body = usermodel.toJson(QJsonDocument::JsonFormat::Indented);response.write(body, true);
}

最后贴出这2个类的源码分别是:
ApiController.h的代码如下:

#pragma once
//解决中文乱码问题。
#pragma execution_character_set("utf-8")
#include <httpserver/httprequesthandler.h>using namespace stefanfrings;//控制器 http://localhost:5050/vue-element-admin/api
class ApiController :public HttpRequestHandler
{Q_OBJECT
public:Q_INVOKABLE ApiController(const ApiController & v){*this = v;}Q_INVOKABLE ApiController &operator=(const ApiController &v){return *this;  }Q_INVOKABLE ApiController(){}//action 登入接口。http://localhost:5050/vue-element-admin/api/loginQ_INVOKABLE  void Login(HttpRequest & request, HttpResponse & response);Q_INVOKABLE void  ApiResult(const QString& msg, int code,HttpRequest & request, HttpResponse & response);/** Generates the response */Q_INVOKABLE void service(HttpRequest& request, HttpResponse& response);void Result(QString msg, int code, HttpResponse & response);~ApiController();
};

ApiController.cpp的代码如下:

#include "ApiController.h"
#include <qjsondocument.h>
#include <qjsonobject.h>
#include <qcryptographichash.h>
#include <qlist.h>Q_INVOKABLE void ApiController::Login(HttpRequest & request, HttpResponse & response)
{//request.//获取post请求的表单 。QMultiMap<QByteArray, QByteArray> forms = request.getParameterMap();//获取用户提交表单中的user_code auto usercode = forms.value("user_code").trimmed();if (usercode.isEmpty()){Result("用户代码不能为空!", 1, response);return;}//获取用户提交表单中的password auto password = forms.value("password").trimmed();if (password.isEmpty()){Result("密码不能为空!", 1, response);return;}//二次判断QByteArray hash = QCryptographicHash::hash(password, QCryptographicHash::Algorithm::Sha256).toBase64();//验证用户代码和密码。QJsonObject object{{"code", 0},{"msg", QJsonValue::Null}};QJsonObject user{{"user_code", "admin"},{"user_name", "admin"}};object.insert("user", user);QJsonDocument usermodel(object);//返回用户信息。QByteArray body = usermodel.toJson(QJsonDocument::JsonFormat::Indented);response.write(body, true);
}Q_INVOKABLE void ApiController::service(HttpRequest & request, HttpResponse & response)
{ }void ApiController::Result(QString msg, int code, HttpResponse & response)
{QJsonObject object{{"code", code},{"msg", QJsonValue(msg)}};QJsonDocument usermodel(object);QByteArray body = usermodel.toJson(QJsonDocument::JsonFormat::Indented);response.write(body, true);
}Q_INVOKABLE void ApiController::ApiResult(const QString& msg,int code,HttpRequest & request, HttpResponse & response)
{ Result(msg, code, response);
}ApiController::~ApiController()
{}

这2个文件的代码属于业务类的代码,都比较简单,实现了用户登入,用户名和密码的验证,上面已经有注释,这里就不进行重点讲解。

添加控制器处理入口

我们更多是希望有所有的请求统一入口,所有的控制器,像刚刚添加的控制器都能够在入口注册。

添加通用控制器处理类 requestmapper.cpp ,该类与业务无关。
主要实现接收请求,并查找控制器和接口方法,并进行转发请求,文件内容如下:

/**@file@author Stefan Frings
*/#include <QCoreApplication>
#include "requestmapper.h"
#include "controller/ApiController.h"
#include <qmetaobject.h>//静态变量
QMultiMap<QString, QString> RequestMapper :: Area;
template <typename T>
int RegisterController(const char *typeName,const QString& area)
{ QByteArray tmp=typeName;  tmp = tmp.toLower();auto type= tmp.constData();int v=qRegisterMetaType<T>(type);if (!area.isEmpty()){RequestMapper::RegisterArea(area, type);}return v;
}
template <typename T>
int RegisterController(const QString& area)
{return RegisterController<T>(T::staticMetaObject.className(), area);
}
RequestMapper::RequestMapper(QObject* parent):HttpRequestHandler(parent)
{qDebug("RequestMapper: created");//注册Api控制器,域为vue-element-admin。访问格式为://http://localhost:5050/{域}/{控制器}//例如 http://localhost:5050/vue-element-admin/apiRegisterController<ApiController>("apicontroller", "vue-element-admin");
}RequestMapper::~RequestMapper()
{qDebug("RequestMapper: deleted");
}void RequestMapper::RegisterArea(const QString &area, const QString& classname)
{Area.insertMulti(area, classname);
}//查找控制器,并调用接口方法。
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 bufferreturn;}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;//判断areaif (id != QMetaType::UnknownType){ if (controls.count() > 0 && !controls.contains(className)){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{qDebug() << "UnknownType service method invokeMethod is error!";}qDebug("RequestMapper: finished request");}

特别说明:当有新的控制器添加的时候,只需要在requestmapper.cpp
文件的函数 RequestMapper 中注册控制器即可,代码如下:

RequestMapper::RequestMapper(QObject* parent):HttpRequestHandler(parent)
{qDebug("RequestMapper: created");//注册Api控制器,域为vue-element-admin。访问格式为://http://localhost:5050/{域}/{控制器}//例如 http://localhost:5050/vue-element-admin/apiRegisterController<ApiController>("apicontroller", "vue-element-admin");
}

使用webengine实现 view

在BitPos 项目 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"
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 buildsearchList.append(binDir + "/../" + appName + "/etc"); // for development with shadow buildsearchList.append(binDir + "/../../" + appName + "/etc"); // for development with shadow buildsearchList.append(binDir + "/../../../" + appName + "/etc"); // for development with shadow buildsearchList.append(binDir + "/../../../../" + appName + "/etc"); // for development with shadow buildsearchList.append(binDir + "/../../../../../" + appName + "/etc"); // for development with shadow buildsearchList.append(QDir::rootPath() + "etc/opt");searchList.append(QDir::rootPath() + "etc");foreach(QString dir, searchList){QFile file(dir + "/" + fileName);if (file.exists()){// foundfileName = QDir(file.fileName()).canonicalPath();qDebug("Using config file %s", qPrintable(fileName));return fileName;}}// not foundforeach(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 fileQString configFileName = searchConfigFile();// Configure and start the TCP listenerQSettings* 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.show(); return a.exec();
}

代码文件目录如下图:

编译并调试View

按f5运行,即可看到接口返回(也可以用浏览器看接口返回),如下图:
源码下载:
https://download.csdn.net/download/m0_49654513/12749026

总结

如果后面添加更多的模块的接口,只需要按模板添加控制器,按操作在控制器内添加方法即可。本节的源码在QQ群561506606共享可以下载。

至此,qt 接口开发演示完毕,下一节讲解qt实现web服务器加载vue应用进行C++和html混合编程。
后面的文章主要与技术有关,索取源码,技术沟通,编译报错,请加QQ群561506606 加群无需验证。
点击链接加入群聊【企业级系统实战-qt vue.j】:https://jq.qq.com/?_wv=1027&k=CCmkgYYu

qt 实现MVC Api控制器开发 web api接口-连载【5】-企业级系统开发实战连载系列 -技术栈(vue、element-ui、qt、c++、sqlite)相关推荐

  1. ASP.NET MVC和ASP.NET Web API跨域请求问题解决方案【最全】

    无论是ASP.NET MVC和ASP.NET Web API跨域请求大致分成2种方式 第一种:web.config 配置 第二种:用HttpContext.Response.AppendHeader设 ...

  2. .NET MVC第九章、Web Api Json序列化与反序列化

    .NET MVC第九章.Web Api Json序列化与反序列化 目录 .NET MVC第九章.Web Api Json序列化与反序列化 json数据格式 JSON 语法 返回对象 Json序列化 反 ...

  3. [Web API] 如何让 Web API 统一回传格式以及例外处理[转]

    [Web API] 如何让 Web API 统一回传格式以及例外处理 前言 当我们在开发 Web API 时,一般的情况下每个 API 回传的数据型态或格式都不尽相同,如果你的项目从头到尾都是由你一个 ...

  4. 用eclipse europa开发web service服务 - 东写西读终见大海无量 - JavaEye技术网站

    用eclipse europa开发web service服务 eclipse europa自带web工具.我们可以使他生成动态web程序.但是在默认情况下,生成的动态默认程序是不包含web servi ...

  5. 使用c++开发web后端接口

    - 能否用c++开发web后端接口? - 答案是:能. Python运算性能比C/C++慢200倍以上. Python Web服务器性能可以达到C/C++的1/10. 参考:https://www.z ...

  6. 【vue开发问题-解决方法】(五)vue Element UI 日期选择器获取日期格式问题 t.getTime is not a function

    [vue开发问题-解决方法](五)vue Element UI 日期选择器获取日期格式问题 t.getTime is not a function 参考文章: (1)[vue开发问题-解决方法](五) ...

  7. 外包项目开发课程整理一:SDLC传统系统开发生命周期7个阶段

    外包项目开发课程整理一:SDLC传统系统开发生命周期7个阶段 前言: 课程全称为:通过案例学习外包项目开发,是软件工程专业大三下的课程,我将根据中方外方ppt教授讲述内容及上网搜索的知识对本课程进行系 ...

  8. 一个基于.Net Core+Vue+Element Ui开发的OA系统

    今天给大家推荐一个开源OA系统. 项目简介 这是一个基于.Net Core构建的简单.跨平台OA系统.企业可以利用它进行信息化建设,框架提供了用户管理.权限管理.表引擎.流程引擎.BI智能报表,可以大 ...

  9. 【我的新书】《良质!PHP企业级系统开发》- 图灵社区

    2019独角兽企业重金招聘Python工程师标准>>> 前言 今年,花了近一年的时间编写了我的第二本书<良质!PHP企业级系统开发>,发表在图灵社区. 感兴趣的,可前往图 ...

  10. z转载:Mc3000开发培训(vb.net智能终端无线系统开发)

    无线终端开发培训文档 项目名称 Mc3000开发培训(vb.net智能终端无线系统开发) 项目号 项目阶段 日期 2006.9.20 工作要素 培训时间 16:00 培训人员 广州雄冠条码公司 培训程 ...

最新文章

  1. 青源 LIVE 预告 | 华为诺亚韩凯:视觉 Transformer 综述
  2. AI真的会杀人?DeepMind开发了二维网格游戏来做测试
  3. 统计 Github 2021 贡献过的开源项目
  4. 先装vs还是先装sql_【家装话题】装修师先装门还是先装地板?
  5. 特斯拉加州工厂无视禁令强行复工,马斯克:要抓就只抓我
  6. apipost脚本使用一
  7. 高恪新路由三硬盘_新路由3 newifi D2路由器刷机breed高恪固件详细教程
  8. Docker-07:Docker网络管理
  9. 小猫爪:PMSM之FOC控制04-SVPWM
  10. stm32f405rgt6芯片手册
  11. Windows Mobile 开发环境搭建
  12. php如何除去图片水印,Phpcms v9如何去掉自带水印的解决方法
  13. 如何解决克隆虚拟出现的Device eth0 does not seem to be present,delaying initialization错误
  14. 转载四代重歼的一片博文 - 漏斗子:“三大战役”完成 人民币国际化就登场
  15. CSS 常见样式 特殊用法 贯穿线徽章箭头
  16. 网络计算机amd,AMD多屏显示设置指南_计算机硬件和网络_IT /计算机_信息
  17. python 一,二维数据的个数化和处理
  18. 微信小程序_在当前页面刷新数据 / 重载当前页面
  19. Python-Opencv 基本操作(三)
  20. 解析button和input type=button 的区别

热门文章

  1. 一个架构师谈什么是架构以及怎么成为一个架构师
  2. 2022年起重机司机(限桥式起重机)报名考试及起重机司机(限桥式起重机)考试资料
  3. word文件打不开,千万别删除!教你轻松修复
  4. ReThought (一): 如何构建理想的开发团队
  5. 信号完整性之铜皮粗糙度
  6. 推荐一款安卓手机一键Root工具
  7. Arch 使用 i3 美化桌面
  8. 双硬盘安装双系统详解
  9. 如何使用Three.js为3D模型构建Color Customizer应用
  10. 优学院中国近现代史纲要试题及答案