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

简介: 为了解决调用一些依赖的如echarts等一些js的代码模块引入的问题,就需要静态文件了。本篇解说StaticFileController,在返回的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里面。

解决

  且发现要分开:
  在这里插入图片描述

  在这里插入图片描述

相关文章
|
10月前
Netty实战: HTTP文件列表服务器
Netty实战: HTTP文件列表服务器
107 0
|
5月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
160 1
|
5月前
|
移动开发 JavaScript 前端开发
HTML5 服务器发送事件(Server-Sent Events)详解
**服务器发送事件(Server-Sent Events, SSE)** 是一种用于构建单向实时通信的技术,允许服务器主动向客户端(通常是浏览器)推送更新。SSE 提供了一个简单的 API,非常适合实时应用,如新闻更新、天气变化、社交媒体通知等。SSE 基于 HTTP 协议,通过长连接实现数据传输。其主要优点包括简洁的 API、持久连接和自动重连功能。大多数现代浏览器都内置了对 SSE 的支持。通过简单的服务器端和客户端代码,可以轻松实现数据的实时推送和接收。
|
7月前
|
机器学习/深度学习 Ubuntu Linux
在Linux中,如何按照该要求抓包:只过滤出访问http服务的,目标ip为192.168.0.111,一共抓1000个包,并且保存到1.cap文件中?
在Linux中,如何按照该要求抓包:只过滤出访问http服务的,目标ip为192.168.0.111,一共抓1000个包,并且保存到1.cap文件中?
|
7月前
|
运维 安全 网络安全
运维笔记:基于阿里云跨地域服务器通信
运维笔记:基于阿里云跨地域服务器通信
349 1
|
10月前
|
SQL DataWorks Java
DataWorks操作报错合集之在阿里云 DataWorks 中,代码在开发测试阶段能够成功运行,但在提交后失败并报错“不支持https”如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
173 1
DataWorks操作报错合集之在阿里云 DataWorks 中,代码在开发测试阶段能够成功运行,但在提交后失败并报错“不支持https”如何解决
|
10月前
|
JSON 自然语言处理 网络协议
【字节跳动青训营】后端笔记整理-2 | Go实践记录:猜谜游戏,在线词典,Socks5代理服务器
猜数字游戏也算是入门一门编程语言必写的程序了。通过这个程序,我们可以熟悉Go语言中的输入输出、流程控制与随机函数的调用。
121 2
|
9月前
|
存储 运维 Serverless
Serverless 使用OOS将http文件转存到对象存储
阿里云OOS提供了一种高效、灵活的解决方案,用于自动化HTTP文件到对象存储的转存。通过OOS,用户可以使用函数计算FC执行Python脚本,直接将文件从HTTP源转移到OSS,无需本地存储或额外ECS实例,降低了成本,提高了效率,并减少了错误。实践步骤包括创建OOS模板并在FC上运行。使用此功能需开通FC服务,创建RAM角色并授权访问OSS。附录中提供了示例模板和Python脚本说明。
|
10月前
|
Web App开发 前端开发 Java
SpringBoot配置HTTPS及开发调试
在实际开发过程中,如果后端需要启用https访问,通常项目启动后配置nginx代理再配置https,前端调用时高版本的chrome还会因为证书未信任导致调用失败,通过摸索整理一套开发调试下的https方案,特此分享
131 0
SpringBoot配置HTTPS及开发调试
|
9月前
|
移动开发 HTML5
详解 HTML5 服务器发送事件(Server-Sent Events)
详解 HTML5 服务器发送事件(Server-Sent Events)
184 0

推荐镜像

更多