Qt+QtWebApp开发笔记(六):http服务器html实现静态相对路径调用第三方js文件
前言
前面做了一些交互,网页是直接通过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文件的更多相关文章
- Django开发笔记六
Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.登录功能完善 登录成功应该是重定向到首页,而不是转发 ...
- Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo
前言 上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍.基础使用,并将其封装一层Qt. 本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt ...
- Qt+ECharts开发笔记(四):ECharts的饼图介绍、基础使用和Qt封装百分比图Demo
前言 前一篇介绍了横向柱图图.本篇将介绍基础饼图使用,并将其封装一层Qt. 本篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口. Demo演示 ...
- Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo
前言 上一篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口. 本篇的demo实现了自动排序的柱状图,实现了一个自动排序柱状图的基本交互方式,即Qt ...
- Qt+MySql开发笔记:Qt5.9.3的msvc2017x64版本编译MySql8.0.16版本驱动并Demo连接数据库测试
前言 mysql驱动版本msvc2015x32版本调好, mysql的mingw32版本的驱动上一个版本编译并测试好,有些三方库最低支持vs2017,所以只能使用msvc2017x64,基于Qt5 ...
- odoo开发笔记 -- 应用服务器&数据库服务器分开部署
app+db在一台服务器: odoo.conf配置文件: db_host = False db_maxconn = 64 db_name = False db_password = 123456db_ ...
- 钉钉开发笔记(六)使用Google浏览器做真机页面调试
注: 参考文献:https://developers.google.com/web/ 部分字段为翻译文献,水平有限,如有错误敬请指正 步骤1: 从Windows,Mac或Linux计算机远程调试And ...
- Qt+ECharts开发笔记(二):Qt窗口动态调整大小,使ECharts跟随Qt窗口大小变换而变换大小
前言 上一篇将ECharts嵌入Qt中,在开始ECharts使用之前,还有一个很重要的功能,就是在窗口变换大小的时候,ECharts的图表尺寸也要跟随Qt窗口变换大小而变换大小. Demo演示 ...
- .Net开发笔记(二十)创建一个需要授权的第三方组件
在使用需要授权的软件时,注册付费的目标是软件的使用者,我们开发人员平时用到的一些第三方组件也是需要授权付费的,也就是说,付费者是开发人员,并不是系统(使用了该第三方组件)的最终使用者. 以上两者的区别 ...
- 【转】Android开发笔记(序)写在前面的目录
原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经 ...
随机推荐
- [转帖]PG Exporter
http://v0.pigsty.cc/zh/docs/reference/kernel-optimize/ Exporter https://github.com/Vonng/pg_exporter ...
- 【转帖】text-davinci-003和ChatGPT之间的不同点
https://zhuanlan.zhihu.com/p/603709081 先看下GPT的发展时间线 InstructGPT(2022 年 1 月)是一系列 GPT-3 模型(包括 text-dav ...
- [转帖]GC 日志
https://www.xjx100.cn/news/188814.html?action=onClick 垃圾回收器的发展历史 1999年:随JDK1.3.1一起来的串行方式Serial GC(第一 ...
- [转帖]优化超大 Nginx 配置导致的内存碎片
https://blog.openresty.com.cn/cn/ngx-cycle-pool-frag/?src=org_news 章亦春发布于 Feb 14, 2023更新于 Mar 2, 202 ...
- 动态添加input,然后获取所有的input框中的值
今天遇见一个问题. 点击按钮,动态添加input框(可以添加多个) 然后搜集用户在input中输入的值. 我刚刚在纠结,给input框中注入事件. 但是这样会很麻烦. 经过同事的指点. 我直接去拿v- ...
- windows幻灯片壁纸
设置为10秒 win+r输入regedit 查找路径 HKEY_CURRENT_USER\Control Panel\Personalization\Desktop Slideshow 修改inter ...
- TienChin 渠道管理-删除渠道
更改一下菜单权限,将删除渠道的 delete 改为 remove: ChannelController.java @PreAuthorize("hasPermission('tienchin ...
- vue 动态路由刷新页面404
1.如果你的静态路由最后有如下代码: // 404 page must be placed at the end !!! { path: "*", redirect: " ...
- PXE+Kickstart 自动化部署系统
PXE 预启动执行环境是由Intel开发的技术,可以让计算机通过网络来启动操作系统(前提是计算机上安装的网卡支持PXE技术),主要用于在无人值守安装系统中引导客户端主机安装Linux操作系统. Kic ...
- Tauri VS. Electron - 真实项目的比较
文章翻译自:Tauri VS. Electron - Real world application 以下是正文: 在这篇文章中我将会用真实的项目来比较 Electron 和 Tauri: Authme ...