前言
最近在开发Qt的http服务器功能,研究比较了一番,选择了QtWebApp方案,相对其他开源库,比较推荐使用该方案,功能齐全,简单易用,如果赶项目的话,极力推荐。
下面介绍一下Qt集成QtWebApp步骤:
一、Qt集成QtWebApp步骤:
1、下载QtWebApp源码:KingJamesGyq/QtWebApp把源码拉下来到本地,将httpsever目录整个移植到自己的项目工程下。
2、在pro文件包含httpserver
include ($$PWD/httpserver/httpserver.pri)
这样就完成集成源码了,剩下的就是根据你自己的业务需求去开发了,细节可以参考Demo1和Demo2。
二、在Qt中使用QtWebApp搭建HTTP服务器
第一步 下载QtWebApp导入工程中
工程示例:
第二步 编写配置文件WebApp.ini
host=192.168.255.128 ;服务ip地址 port=8080 ;端口号 minThreads=4 ;4个线程始终保持运行状态 maxThreads=100 ;并发工作线程的最大数量 cleanupInterval=60000 readTimeout=60000 maxRequestSize=16000 maxMultiPartSize=10000000
第三步 加载配置文件,创建HTTP侦听器对象
main.cpp
#include "httplistener.h" #include "httprequesthandler.h" #include "RequestMapper.h" //自定义类请求映射器类 QSettings* listenerSettings= new QSettings(QCoreApplication::applicationDirPath()+"/WebApp.ini",QSettings::IniFormat); new stefanfrings::HttpListener(listenerSettings, new RequestMapper());
第四步 自定义类请求映射器类
RequestMapper.h
#ifndef REQUESTMAPPER_H #define REQUESTMAPPER_H #include "httprequesthandler.h" using namespace stefanfrings; #include "HelloController.h" class RequestMapper:public HttpRequestHandler { public: RequestMapper(); void service(HttpRequest& request, HttpResponse& response); private: HelloController m_helloController; }; #endif // REQUESTMAPPER_H
RequestMapper.cpp
#include "RequestMapper.h" #include "json.hpp" #include <string> using namespace std; using namespace nlohmann; RequestMapper::RequestMapper() { } void RequestMapper::service(HttpRequest &request, HttpResponse &response) { QByteArray path=request.getPath(); QByteArray method = request.getMethod(); QByteArray nm = request.getParameter("nm"); qDebug() << "RequestMapper: path=" << path.data(); qDebug() << "RequestMapper: method=" << method.data(); qDebug() << "RequestMapper: nm=" << nm.data(); if ( path=="/hello") { m_helloController.service(request,response); } }
第五步 自定义业务请求处理类
HelloController .h
#ifndef HELLOCONTROLLER_H #define HELLOCONTROLLER_H #include <QObject> #include "httprequesthandler.h" using namespace stefanfrings; #include "json.hpp" using namespace nlohmann; #include <string> using namespace std; class HelloController : public QObject { Q_OBJECT public: explicit HelloController(QObject *parent = 0); void service(HttpRequest& request, HttpResponse& response); signals: public slots: }; #endif // HELLOCONTROLLER_H
HelloController.cpp
#include "HelloController.h" HelloController::HelloController(QObject *parent) : QObject(parent) { } void HelloController::service(HttpRequest &request, HttpResponse &response) { QByteArray body = request.getBody(); qDebug() << body; json res; res["code"] = 200; res["mesage"] = "success"; string str = res.dump(4); // response.setStatus(404); response.write(str.c_str()); }
第六步 测试
服务运行示例
浏览器发请求示例
另外:其他的映射和控制器写法:
requestmapper.h
#ifndef REQUESTMAPPER_H #define REQUESTMAPPER_H #include "QtWebApp/httpserver/httprequesthandler.h" using namespace stefanfrings; /* The request mapper dispatches incoming HTTP requests to controller classes depending on the requested path. */ class RequestMapper : public HttpRequestHandler { Q_OBJECT Q_DISABLE_COPY(RequestMapper) public: /* Constructor. @param parent Parent object */ RequestMapper(QObject* parent=0); /* Destructor. */ ~RequestMapper(); /* Dispatch incoming HTTP requests to different controllers depending on the URL. @param request The received HTTP request @param response Must be used to return the response */ void service(HttpRequest& request, HttpResponse& response); // 拦截未授权操作 bool preHandle(HttpRequest& request, HttpResponse& response); }; #endif // REQUESTMAPPER_H
requestmapper.cpp
#include "requestmapper.h" #include "global.h" #include "controller/logincontroller.h" #include "controller/funcvercodecontroller.h" #include "controller/sysmsgcontroller.h" #include "controller/sysusercontroller.h" #include "controller/commoncontroller.h" #include "controller/sysmenucontroller.h" #include "controller/sysdiccontroller.h" #include "controller/sysrolecontroller.h" #include "controller/sysdeptcontroller.h" #include "controller/syslogcontroller.h" #include "controller/funcmonitorcontroller.h" #include "controller/sysusermsgcontroller.h" #include "controller/funcsmscontroller.h" #include "controller/funcemailcontroller.h" #include "controller/funcloadcontroller.h" #include "controller/funcexportcontroller.h" #include<qthread.h> RequestMapper::RequestMapper(QObject* parent) :HttpRequestHandler(parent) { qDebug("RequestMapper: created"); qDebug()<<".................RequestMapper created......................currentThreadId():"<<QThread::currentThreadId(); } RequestMapper::~RequestMapper() { qDebug("RequestMapper: deleted"); } void RequestMapper::service(HttpRequest& request, HttpResponse& response) { qDebug()<<"..............RequestMapper::service..................currentThreadId():"<<QThread::currentThreadId(); QByteArray path = request.getPath().toLower(); QString tmp = QString(path); qDebug("RequestMapper::service e"); //实现跨域访问,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,access_token"); response.setHeader("Access-Control-Max-Age", "86400"); response.setHeader("Vary", "Accept-Encoding,Origin"); response.setHeader("Keep-Alive", "timeout=2,max=99"); response.setHeader("Access-Control-Allow-Credentials", "true"); //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; } //拦截部分,做token校验 if (path.startsWith("/func") || path.startsWith("/sys") || path.startsWith("/monitor/")) { if(!preHandle(request, response)) return; } if (path.startsWith("/login") || path.startsWith("/logout")) { LoginController().service(request, response); } else if (path.startsWith("/vercode")) { FuncVercodeController().service(request, response); } else if (path.startsWith("/func/message/user/")) { SysUserMsgController().service(request, response); } else if (path.startsWith("/func/message/sys/")) { SysMsgController().service(request, response); } else if (path.startsWith("/sys/user/")) { SysUserController().service(request, response); } else if (path.startsWith("/sys/menu/")) { SysMenuController().service(request, response); } else if (path.startsWith("/sys/dic/")) { SysDicController().service(request, response); } else if (path.startsWith("/sys/role/")) { SysRoleController().service(request, response); } else if (path.startsWith("/sys/dept/")) { SysDeptController().service(request, response); } else if (path.startsWith("/sys/log/")) { SysLogController().service(request, response); } else if (path.startsWith("/func/sms/")) { FuncSmsController().service(request, response); } else if (path.startsWith("/sys/email/")) { FuncEmailController().service(request, response); } else if (path.startsWith("/func/upload/")) { FuncLoadController().service(request, response); } else if (path.startsWith("/func/export/")) { FuncExportController().service(request, response); } else if (path.startsWith("/monitor")) { FuncMonitorController().service(request, response); } else if (path.startsWith("/common")) { CommonController().service(request, response); } else { staticFileController->service(request, response); } qDebug("RequestMapper: finished request"); // Clear the log buffer if (logger) { // logger->clear(); } } // 拦截未授权操作 bool RequestMapper::preHandle(HttpRequest& request, HttpResponse& response) { // token校验不通过,返回1001 ResultJson result(1001, false, QStringLiteral("token已失效")); QString accessToken = request.getHeader("access_token"); if(accessToken == NULL || accessToken.isEmpty()) accessToken = request.getParameter("access_token"); if(accessToken == NULL || accessToken.isEmpty()) { result.failedMsg(QStringLiteral("找不到token")); ResponseUtil::replyJson(response, result); //staticFileController->service(request, response); return false; } //根据token登录 QString username = JwtUtil::getUsername(accessToken); if(username == NULL || username.isEmpty()) { ResponseUtil::replyJson(response, result); //staticFileController->service(request, response); return false; } //判断token是否有效 QString token_key = username + "_token"; QString saveToken = CacheApi::instance()->get(token_key); if(saveToken != accessToken) { ResponseUtil::replyJson(response, result); //staticFileController->service(request, response); return false; } //刷新token CacheApi::instance()->insert(token_key, accessToken, JwtUtil::EXPIRED_TIME); return true; }
logincontroller.h
#ifndef LOGINCONTROLLER_H #define LOGINCONTROLLER_H #include "global.h" #include <QMap> using namespace stefanfrings; class LoginController: public HttpRequestHandler { Q_OBJECT Q_DISABLE_COPY(LoginController) public: LoginController(); void service(HttpRequest& request, HttpResponse& response); void login(HttpRequest& request, HttpResponse& response); //登录 void logout(HttpRequest& request, HttpResponse& response); //退出 private: SqlHelper* m_pHelper; typedef void (LoginController::*pServFunc)(HttpRequest& request, HttpResponse& response); QMap<QString, pServFunc> m_mapFunc; }; #endif // LOGINCONTROLLER_H
logincontroller.cpp
#include "logincontroller.h" #include <QCryptographicHash> #include <qthread.h> LoginController::LoginController() { qDebug()<<"....................LoginController........................currentThreadId():"<<QThread::currentThreadId(); // m_pHelper = SqlHelper::instance(); m_pHelper = new SqlHelper(); m_pHelper->initDB("QtWebAdmin.db"); m_mapFunc.insert("/login", &LoginController::login); //登录 m_mapFunc.insert("/logout", &LoginController::logout); //登出 } void LoginController::service(HttpRequest& request, HttpResponse& response) { qDebug()<<"...................LoginController::service........................currentThreadId():"<<QThread::currentThreadId(); QByteArray path = request.getPath().toLower(); if(m_mapFunc.contains(path)) { pServFunc func = m_mapFunc.value(path); (this->*func)(request, response); } } void LoginController::login(HttpRequest& request, HttpResponse& response) { qDebug()<<"..................LoginController::login...................currentThreadId():"<<QThread::currentThreadId(); ResultJson ret(0, true, QStringLiteral("登入成功")); QString body = request.getBody(); QString username = request.getParameter("username"); QString password = request.getParameter("password"); QString vercode = request.getParameter("vercode"); QString access_token = request.getParameter("access_token"); QString codeid = request.getParameter("codeid"); HttpSession session = sessionStore->getSession(request, response, true); if(username.isEmpty()) { ret.failedMsg(QStringLiteral("用户名为空")); response.write(ret.toString().toLocal8Bit()); return; } if(password.isEmpty()) { ret.failedMsg(QStringLiteral("密码为空")); response.write(ret.toString().toLocal8Bit()); return; } if(vercode.isEmpty()) { ret.failedMsg(QStringLiteral("验证码为空")); response.write(ret.toString().toLocal8Bit()); return; } // // if(!session.contains("vercode")) // { // ret.failedMsg(QStringLiteral("未找到验证码")); // response.write(ret.toString().toLocal8Bit()); // return; // } //跨域情况下,以网页请求时附带UUID,作为验证码标志 QString sessionCode = CacheApi::instance()->get(codeid + "_vercode"); if(sessionCode.isNull()) { ret.failedMsg(QStringLiteral("未找到验证码或验证码已过期")); response.write(ret.toString().toLocal8Bit()); return; } //QString sessionCode = session.get("vercode").toString(); if(sessionCode.toLower() != vercode.toLower()) { ret.failedMsg(QStringLiteral("验证码不匹配")); response.write(ret.toString().toLocal8Bit()); return; } //................检查密码 SysUser loginUser; if(!m_pHelper->selectUserByName(loginUser, username)) { ret.failedMsg(QStringLiteral("未找到用户或密码不正确!")); response.write(ret.toString().toLocal8Bit()); return; } SysLog log("system", SysLog::LOGIN, QStringLiteral("用户登录"), username, QString(__FILE__), QString(__FUNCTION__), GlobalFunc::JsonToString(request.getParameterJson()), request.getPeerAddress().toString(), request.getHeader("User-Agent"), QStringLiteral("用户成功登录")); QString pwdAndSalt = password + loginUser.getSalt(); QString md5Pwd = QString(QCryptographicHash::hash(pwdAndSalt.toLocal8Bit(), QCryptographicHash::Md5).toHex()); if(md5Pwd != loginUser.getPassword()) { ret.failedMsg(QStringLiteral("密码不正确!")); response.write(ret.toString().toLocal8Bit()); log.setMemo(QStringLiteral("密码不正确!")); m_pHelper->insertEntity(&log); return; } //JWT获取,并存入缓存 QString accessToken = JwtUtil::sign(username, password); CacheApi::instance()->insert(username + "_token", accessToken, JwtUtil::EXPIRED_TIME); //session保存账户名和登录时间 session.set("username", username); session.set("logintime", QTime::currentTime()); //返回token ret.setData("access_token", accessToken); ResponseUtil::replyJson(response, ret); m_pHelper->insertEntity(&log); } // /logout?access_token=? void LoginController::logout(HttpRequest& request, HttpResponse& response) { QString access_token = request.getParameter("access_token"); QString username = JwtUtil::getUsername(access_token); CacheApi::instance()->remove(username + "_token"); QJsonObject objParam = request.getParameterJson(); objParam.insert("access_token", "?"); // ResponseUtil::replyJson(response, ResultJson(0, true, QStringLiteral("操作成功"))); SysLog log("system", SysLog::LOGOUT, QStringLiteral("用户退出"), username, QString(__FILE__), QString(__FUNCTION__), GlobalFunc::JsonToString(objParam), request.getPeerAddress().toString(), request.getHeader("User-Agent"), QStringLiteral("用户退出成功")); m_pHelper->insertEntity(&log); }