前言

  前面做了一些交互,网页是直接通过html对response进行返回的,这里QtWebApp与传统的web服务器不同,传统的web服务器可以调用同级目录相对路径或者绝对路径下的js,而QtWebApp的httpserver是response返回当前页面的问题,默认是无法调用的。
  为了解决调用一些依赖的如echarts等一些js的代码模块引入的问题,就需要静态文件了。
本篇解说StaticFileController,在返回的html文本中调用外部js文件,类似的,其他文件都是一样了,只是引入的后缀名不一样。

 

Demo

  这里是调用静态文件js的
  

  这里是重定向测试的
  

 

静态文件(重要功能,如调用服务器的js文件等)

  如果QtWebapp无法传递存储在服务器文件夹中的静态文件,那么它将是不完整的。StaticFileController提供了这一功能。但在使用它之前,需要在ini文件中进行一些额外的配置设置:

[files]
path=../docroot
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000
maxCachedFileSize=65536

  

  • path:设置指定存储静态文件的基本文件夹。它是相对于配置文件的。还可以编写绝对路径名,如“/opt/server/docroot”或“C:/server/docroot”。
  • encoding:encoding参数仅用于*.txt和*.html文件,用于告诉浏览器这些文件的编码方式。如果同时需要不同的编码,则必须创建StaticFileController的多个实例——每个编码一个。
    其他参数控制高速缓存。首先,应该知道操作系统已经缓存了文件。然而,发现Linux和Windows在处理小文件时都表现不佳。因此,建议使用应用程序内部缓存,但仅适用于小文件。
  • cacheTime:cacheTime控制文件在内存中最多保存多少毫秒。值0表示,只要有足够的空间,文件就会保留在内存中。
  • cacheSize:cacheSize指定允许缓存占用的内存量。一兆字节是一个很好的开始值。如果用户请求的文件不在缓存中,则会删除最旧的文件,为新文件腾出空间。
  • maxCachedFileSize:maxCachedFileSize控制缓存中单个文件的最大大小。web服务器应用程序不会缓存较大的文件。但正如所写的那样,操作系统可以很好地缓存大文件。事实证明,64千字节是一个很好的开始值。
  • maxAge:maxAge参数的含义与cacheTime基本相同,但控制网络浏览器的缓存,而不是服务器。

  需要一个指向StaticFileController实例的全局指针,以便整个程序都可以访问它。首先添加到
global.h:

#ifndef GLOBAL_H
#define GLOBAL_H #include "httpsessionstore.h"
#include "staticfilefontroller.h" using namespace stefanfrings; extern HttpSessionStore* sessionStore;
extern StaticFileController* staticFileController; #endif // GLOBAL_H

  global.cpp:

#include "global.h"

HttpSessionStore* sessionStore;
StaticFileController* staticFileController;

  在main.cpp中,配置StaticFileController的实例:

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QString configFileName=searchConfigFile(); // Session store
QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
sessionSettings->beginGroup("sessions");
sessionStore=new HttpSessionStore(sessionSettings,&app); // Static file controller
QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
fileSettings->beginGroup("files");
staticFileController=new StaticFileController(fileSettings,&app); // HTTP server
QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
listenerSettings->beginGroup("listener");
new HttpListener(listenerSettings,new RequestMapper(&app),&app); return app.exec();
}

  现在可以在requestmapper.cpp中使用staticFileController:

#include "requestmapper.h"
#include "httpsession.h"
#include "global.h" void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data()); if (path=="/" || path=="/hello") {
helloWorldController.service(request, response);
}
else if (path=="/list") {
listDataController.service(request, response);
}
else if (path=="/login") {
loginController.service(request, response);
}
else if (path=="/cookie") {
cookieTestController.service(request, response);
}
else if (path.startsWith("/files")) {
staticFileController->service(request,response);
}
else {
response.setStatus(404,"Not found");
response.write("The URL is wrong, no such document.");
} qDebug("RequestMapper: finished request");
}

  现在创建文件夹MyFirstWebApp/docroot/files,然后创建一个名为hello.HTML的HTML文件:

<html>
<body>
Hello World!
</body>
</html>

  启动程序并打开http://localhost:8080/files/hello.html.浏览器将接收该文件的内容。
  可以将其他文件(图像、css、javascript…)添加到该文件夹中,如果愿意,还可以创建更多的子文件夹。
  如果出现“找不到文件”错误,调试消息将帮助找出服务器真正试图加载的文件。

 

HTTP重定向

  有时想将浏览器重定向到另一个页面。这通常用于需要用户登录的网站。如果用户没有登录,他会被重定向到登录页面。当然,匿名用户必须可以访问登录页面本身。
  requestmapper.cpp中的更改:

void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data()); QByteArray sessionId=sessionStore->getSessionId(request,response);
if (sessionId.isEmpty() && path!="/login") {
qDebug("RequestMapper: redirect to login page");
response.redirect("/login");
return;
} else if (path=="/login") {
...
} else if (path=="/whatever") {
...
} qDebug("RequestMapper: finished request");
}

国际化(ps:在文本中返回中文)

  HTTP服务器总是使用QByteArray而不是QString,原因很简单:性能。整个HTTP协议都是基于8位编码的,所以决定不浪费CPU时间,不必要地来回转换。
但是当然可以使用Unicode。例子:

void UnicodeController::service(HttpRequest& request, HttpResponse& response) {
QString chinese=QString::fromUtf8("美丽的花朵需要重症监护");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.write(chinese.toUtf8(),true);
}

  这是谷歌翻译(不会说中文)提供的“美丽的花朵需要重症监护”的中文翻译。
  从QString到UTF-8的转换并不比到Latin1的转换慢。因此,如果需要,请随时使用Unicode。但千万不要忘记使用QString::fromUtf8。如果只写中文=“美丽的花朵需要重症监护“,只会得到乱码。

 

Demo增量:实战配置加载静态文件

步骤一:准备代码模板

  准备之前的demo v1.4.0模板:

maxCachedFileSize=65536

步骤二:新增静态文件管理类用于全局使用

  
  

步骤三:新增静态配置

  新增静态配置,路径调整问exe当前的子目录www(符合后端基本习惯)

[files]
path=../www
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000

步骤四:初始化静态文件

  
  

步骤五:在Index进行路径文件分流

  必须分流,静态文件指示的api和文件是先从处理过程然后再到静态文件管理类的,所以有些是请求数据则需要在代码中处理,这里之前是没有这样做,可查看“入坑一”。
  

  本Demo无法打开跳转的staticFileUserJs,可查看“入坑二”
  本Demo静态文件无法调用js,请查看“入坑三”。

 

模块化(有一些新增调整)

  

 

Demo源码

etc/httpServer.ini(新增files)

[listener]
;ip=127.0.0.1
port=8080
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=10000000
minThreads=4
maxThreads=100
cleanupInterval=60000 [logging]
fileName=../logs/httpserver.log
;fileName=/dev/stdout
minLevel=CRITICAL
bufferSize=100
maxSize=1000000
maxBackups=2
;timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
timestampFormat=yyyy-MM-dd hh:mm:ss.zzz
msgFormat={timestamp} {typeNr} {type} {thread} {msg}
;QT5 supports: msgFormat={timestamp} {typeNr} {type} {thread} {msg}\n in {file} line {line} function {function} [files]
path=../www
encoding=UTF-8
maxAge=90000
cacheTime=60000
cacheSize=1000000
maxCachedFileSize=65536

www/index.html(新增文件,静态文件主页)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>长沙红胖子Qt</title>
</head>
<body>
<p>你好, 长沙红胖子 QQ:21497936 www.hpzwl.com</p>
<p><a href="helloworld">Hello world!</a></p>
<p><a href="list">list</a></p>
<p><a href="login">login</a></p>
<p><a href="checkState">checkState</a></p>
<p><a href="staticFileUseJs.html">staticFileUseJs</a></p>
</body>

www/staticFileUseJs.html(新增文件,测试跳转和js调用)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>长沙红胖子Qt</title>
</head>
<body>
<p><a>这里是检测状态Demo v1.5.0了</a></p>
<p><a id="dt1">123.567</a></p>
<p><a id="dt2">123.567</a></p>
<p><button onclick="reset()">清空</button></p>
<p><button onclick="getDt1()">获取</button></p>
<p><button onclick="doJs()">调用js</button></p>
<script>
function reset() {
document.getElementById("dt1").innerHTML="---.---";
document.getElementById("dt2").innerHTML="---.---";
document.getElementById("dt3").innerHTML="---.---";
}
function getDt1() { var xhr = new XMLHttpRequest();
xhr.open('GET','/checkState/data',true);
xhr.send();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200)
{
document.getElementById("dt1").innerHTML = xhr.responseText;
}
}
}
</script>
<!-- 不成功引入js,引入js脚本要script分开,引入失败则进入界面不会弹出alert -->
<script src="jquery.min.js"></script>
<script>
function doJs() {
alert($("li")[0]);
}
</script>
</body>

StaticFileManager.h(全局使用静态文件消息处理)

#ifndef STATICFILEMANAGER_H
#define STATICFILEMANAGER_H #include <QObject>
#include <QMutex> #include "httplistener.h"
#include "staticfilecontroller.h" using namespace stefanfrings; class StaticFileManager : public QObject
{
Q_OBJECT
private:
explicit StaticFileManager(QObject *parent = 0); public:
static StaticFileManager *getInstance(); public:
StaticFileController *getStaticFileController() const; public:
void setStaticFileController(StaticFileController *pStaticFileController); private:
static StaticFileManager *_pInstance;
static QMutex _mutex; private:
StaticFileController *_pStaticFileController;
}; #endif // STATICFILEMANAGER_H

StaticFileManager.cpp

#include "StaticFileManager.h"

#include "IndexRequestHandler.h"
#include "HttpSessionStoreManager.h" #include <QApplication>
#include <QDir> #include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") StaticFileManager *StaticFileManager::_pInstance = 0;
QMutex StaticFileManager::_mutex; StaticFileManager::StaticFileManager(QObject *parent)
: QObject(parent),
_pStaticFileController(0)
{ } StaticFileManager *StaticFileManager::getInstance()
{
if(!_pInstance)
{
QMutexLocker lock(&_mutex);
if(!_pInstance)
{
_pInstance = new StaticFileManager();
}
}
return _pInstance;
} StaticFileController *StaticFileManager::getStaticFileController() const
{
return _pStaticFileController;
} void StaticFileManager::setStaticFileController(StaticFileController *pStaticFileController)
{
_pStaticFileController = pStaticFileController;
}

IndexRequestHandler.h

#ifndef INDEXREQUESTHANDLER_H
#define INDEXREQUESTHANDLER_H #include "httprequesthandler.h" #include "HelloWorldRequestHandler.h"
#include "ListRequestHandler.h"
#include "LoginRequestHandler.h"
#include "CheckStateRequestHandler.h" using namespace stefanfrings; class IndexRequestHandler : public HttpRequestHandler
{
public:
IndexRequestHandler(QObject *parent = 0); public:
void service(HttpRequest& request, HttpResponse& response); private:
QTextCodec *_pTextCodec; private:
HelloWorldRequestHandler _helloWorldRequestHandler; // hellowold消息处理
ListRequestHandler _listRequestHandler; // list消息处理 LoginRequestHandler _loginRequestHandler; // login消息处理,Demo v1.3.0 CheckStateRequestHandler _checkStateRequestHandler; // checkState实时检测状态
}; #endif // INDEXREQUESTHANDLER_H

IndexRequestHandler.cpp(调整了入口和放开静态文件)

#include "IndexRequestHandler.h"
#include "StaticFileManager.h" #include <QTextCodec> #include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz") using namespace stefanfrings; IndexRequestHandler::IndexRequestHandler(QObject *parent)
: HttpRequestHandler(parent)
{
// 返回文本(我们需要在浏览器上看,所以将Qt内部编码都转成GBK输出即可,不管他本身是哪个编码)
// WINDOWS: GBK GB2312
// LINUX : urf-8
// _pTextCodec = QTextCodec::codecForName("utf-8");
_pTextCodec = QTextCodec::codecForName("GBK");
} void IndexRequestHandler::service(HttpRequest &request, HttpResponse &response)
{ QString path = request.getPath();
LOG << path; if(path == "/" || path == "/index")
{
#if 0
// index使用代码
QString str;
str += "<!DOCTYPE html>"
"<html lang=\"en\">"
"<head>"
"<meta charset=\"UTF-8\">"
"<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<title>长沙红胖子Qt</title>"
"</head>"
"<body>"
" <p>你好, 长沙红胖子 QQ:21497936 www.hpzwl.com</p>"
" <p><a href=\"helloworld\">Hello world!</a></p>"
" <p><a href=\"list\">list</a></p>"
" <p><a href=\"login\">login</a></p>"
" <p><a href=\"checkState\">checkState</a></p>"
" <p><a href=\"staticFileUseJs.html\">staticFileUseJs</a></p>"
"</body>";
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
#else
// index使用文件
LOG << path;
StaticFileManager::getInstance()->getStaticFileController()->service(request, response);
#endif
}else if(path == "/helloworld")
{
_helloWorldRequestHandler.service(request, response);
}else if(path == "/list")
{
_listRequestHandler.service(request, response);
}else if(path == "/login" || path == "/login/out")
{
_loginRequestHandler.service(request, response);
}else if(path == "/checkState" || path == "/checkState/data")
{
_checkStateRequestHandler.service(request, response);
}else if( path.startsWith("/staticFileUseJs")
|| path == "/favicon.ico"
|| path.endsWith(".js"))
{
LOG << path;
StaticFileManager::getInstance()->getStaticFileController()->service(request, response);
}else {
#if 0
LOG;
response.setStatus(404,"Not found");
QString str;
str = "The URL is wrong, no such document.";
QByteArray byteArray = str.toUtf8();
response.write(byteArray);
#else
// 这里进行重定向
LOG << "重定向至/";
response.redirect("/");
#endif
}
}
 

工程模板v1.5.0

  

 

入坑

入坑一:直接从listerner切入静态文件后,ajax也是请求路径

问题

  Ajax未代码捕捉,直接入静态文件本身就无法变成api接口了。
  
  

原因

  加载的静态文件,里面请求的任何东西只要调用静态文件处理类来处理,则会都变成本地静态文件(这里静态文件主要是可以调用未传递过去到客户端的文件,如.js文件等)

解决

  先得从一个头部文件开始分流,一开始使用一个自定义的代码消息处理,这个消息处理通过第一层路径或者子路径来判断是否是静态文件,后再扔给静态文件,就可以绕开。
  而接口也是通过路径进行判断,然后用代码进行返回,所以这种开发起来就混合了Qt和httpJs等静态文件了。

入坑二:添加js静态文件后直接跑飞404

问题

  

原因

  去掉js的代码,未恢复正常
  去掉js的文件,未恢复正常
  检查代码发现,是重定向问题
  
  请查询发现,favicon,即Favorites Icon的缩写,顾名思义,便是其可以让浏览器的收藏夹中除显示相应的标题外,还以图标的方式区别不同的网站,就是网站的图标。
  没有图标本来为空,而我们没有分流该路径,分流之后还是不行,再测试:
  

  

  经过摸索,还发现:
  
  再后来发现,静态文件这个是文件,需要后缀html,因为我们做qt的这块使用代码api习惯了,导致忽略了这点

解决

  

入坑三:加载了js静态文件未弹窗

问题

  
  但是点击没有弹窗:
  

原因

  检查,未放开.js,加载js没有真的加载进去
  在静态文件分流的地方,放开后缀.js的
  

  还是不行,测试发现必须引入js在单独空的script里面。

解决

  且发现要分开:
  

  

Qt+QtWebApp开发笔记(六):http服务器html实现静态相对路径调用第三方js文件的更多相关文章

  1. Django开发笔记六

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.登录功能完善 登录成功应该是重定向到首页,而不是转发 ...

  2. Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo

    前言   上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍.基础使用,并将其封装一层Qt.  本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt ...

  3. Qt+ECharts开发笔记(四):ECharts的饼图介绍、基础使用和Qt封装百分比图Demo

    前言   前一篇介绍了横向柱图图.本篇将介绍基础饼图使用,并将其封装一层Qt.  本篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口.   Demo演示 ...

  4. Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo

    前言   上一篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口.  本篇的demo实现了自动排序的柱状图,实现了一个自动排序柱状图的基本交互方式,即Qt ...

  5. Qt+MySql开发笔记:Qt5.9.3的msvc2017x64版本编译MySql8.0.16版本驱动并Demo连接数据库测试

    前言   mysql驱动版本msvc2015x32版本调好, mysql的mingw32版本的驱动上一个版本编译并测试好,有些三方库最低支持vs2017,所以只能使用msvc2017x64,基于Qt5 ...

  6. odoo开发笔记 -- 应用服务器&数据库服务器分开部署

    app+db在一台服务器: odoo.conf配置文件: db_host = False db_maxconn = 64 db_name = False db_password = 123456db_ ...

  7. 钉钉开发笔记(六)使用Google浏览器做真机页面调试

    注: 参考文献:https://developers.google.com/web/ 部分字段为翻译文献,水平有限,如有错误敬请指正 步骤1: 从Windows,Mac或Linux计算机远程调试And ...

  8. Qt+ECharts开发笔记(二):Qt窗口动态调整大小,使ECharts跟随Qt窗口大小变换而变换大小

    前言   上一篇将ECharts嵌入Qt中,在开始ECharts使用之前,还有一个很重要的功能,就是在窗口变换大小的时候,ECharts的图表尺寸也要跟随Qt窗口变换大小而变换大小.   Demo演示 ...

  9. .Net开发笔记(二十)创建一个需要授权的第三方组件

    在使用需要授权的软件时,注册付费的目标是软件的使用者,我们开发人员平时用到的一些第三方组件也是需要授权付费的,也就是说,付费者是开发人员,并不是系统(使用了该第三方组件)的最终使用者. 以上两者的区别 ...

  10. 【转】Android开发笔记(序)写在前面的目录

    原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经 ...

随机推荐

  1. [转帖]springboot中使用skywalking实现日志追踪

    文章目录 SkyWalking分布式追踪系统 介绍 主要架构 环境 引入依赖 配置Log4j2 下载编译好的8.7.0版本包 使用探针实现日志追踪 启动脚本 启动Java服务 访问服务 使用UI 切换 ...

  2. 关于IO性能的一些学习与了解

    关于IO性能的一些学习与了解 摘要 最近心气不高. 学习进度也拖的比较慢. 以后想能够多为自己着想.自己有自己的节奏, 不能只为别人考虑. 要改变一下自己的做事风格. 一些事情想帮则帮, 不想帮就当看 ...

  3. Spring Boot接口设计

    项目文件结构 编写示例代码 添加lombok的依赖 新建DemoController,用于提供RESTful接口.增加相关注解:@RestController,@RequestMapping(&quo ...

  4. 在Linux Ubuntu系统中部署C++环境与Visual Studio Code软件

      本文介绍在Linux Ubuntu操作系统下,配置Visual Studio Code软件与C++代码开发环境的方法.   在文章VMware虚拟机部署Linux Ubuntu系统的方法中,我们介 ...

  5. Fabric-ca server端初始化过程源码分析

    本文从Fabric-ca源码入手,简单分析server启动时的过程.Fabric-ca源码可以从github.com下载,本文以v1.4.6为例进行简单分析. Fabric-ca是有go语言编写的,与 ...

  6. 不同版本的Unity要求的NDK版本和两者对应关系表(Unity NDK Version Match)

    IL2CPP需要NDK Unity使用IL2CPP模式出安卓包时,需要用到NDK,如果没有安装则无法导出Android Studio工程或直接生成APK,本篇记录一下我下载NDK不同版本的填坑过程. ...

  7. Spring框架源码分析

    目录 Spring核心思想 Spring源码编译 自定义实现Spring框架IOC与DI Spring源码Ioc核心模块分析 BeanDefinition整体介绍 FactoryBean接口的使用 B ...

  8. ChatGenTitle:使用百万arXiv论文信息在LLaMA模型上进行微调的论文题目生成模型

    ChatGenTitle:使用百万arXiv论文信息在LLaMA模型上进行微调的论文题目生成模型 相关信息 1.训练数据集在Cornell-University/arxiv,可以直接使用: 2.正式发 ...

  9. 解决:VScode中 import 后出现no module的问题

    问题: ModuleNotFoundError: No module named 'xxx' 除去没有安装包的问题 这个问题还是挺难受的,pycharm和终端都可以运行,只有vscode报错 方法一: ...

  10. C/C++ 获取主机网卡MAC地址

    MAC地址(Media Access Control address),又称为物理地址或硬件地址,是网络适配器(网卡)在制造时被分配的全球唯一的48位地址.这个地址是数据链路层(OSI模型的第二层)的 ...