Qt+QtWebApp开发笔记(四):http服务器使用Session和Cookie实现用户密码登录和注销功能

news2025/1/23 4:03:34

若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130921152

红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…

Qt开发专栏:三方库开发技术

上一篇:《Qt+QtWebApp开发笔记(三):http服务器动态html连接跳转基础交互》
下一篇:敬请期待…


前言

  前面实现了基础的跳转,那么动态交互中登录是常用功能。
  本篇实现一个动态交互的简单登录和注销功能,在Qt中使用Session和Cookie技术。


Demo

  在这里插入图片描述

下载地址

  链接:https://pan.baidu.com/s/1nkmsHgr-11Khe9k6Ntyf_g?pwd=1234


Html处理用户输入Session与Cookie

表单登录submit

  Web应用程序通常处理用户输入。将开发一个登录表单,看看进展如何。
创建一个名为LoginController的新类。同样,它是从HttpRequestHandl派生的

logincontroller.h:

#ifndef LOGINCONTROLLER_H
#define LOGINCONTROLLER_H

#include "httprequesthandler.h"

using namespace stefanfrings;

class LoginController : public HttpRequestHandler {
    Q_OBJECT
public:
    LoginController(QObject* parent=0);
    void service(HttpRequest& request, HttpResponse& response);
};

#endif // LOGINCONTROLLER_H

logincontroller.cpp:

#include "logincontroller.h"

LoginController::LoginController(QObject* parent)
    :HttpRequestHandler(parent) {
    // empty
}

void LoginController::service(HttpRequest &request, HttpResponse &response) {
    QByteArray username=request.getParameter("username");
    QByteArray password=request.getParameter("password");

    qDebug("username=%s",username.constData());
    qDebug("password=%s",password.constData());

    response.setHeader("Content-Type", "text/html; charset=UTF-8");
    response.write("<html><body>");

    if (username=="test" and password=="hello") {
        response.write("Yes, correct");
    }
    else {
        response.write("<form method='POST' action='/login'>");
        if (!username.isEmpty()) {
            response.write("No, that was wrong!<br><br>");
        }
        response.write("Please log in:<br>");
        response.write("Name:  <input type='text' name='username'><br>");
        response.write("Password: <input type='password' name='password'><br>");
        response.write("<input type='submit'>");
        response.write("</form");
    }

    response.write("</body></html>",true);
}

  (PS:html代表是提交表单)
  将这个新控制器添加到请求映射器中,修改requestmapper.h:

#ifndef REQUESTMAPPER_H
#define REQUESTMAPPER_H

#include "httprequesthandler.h"
#include "helloworldcontroller.h"
#include "listdatacontroller.h"
#include "logincontroller.h"

class RequestMapper : public HttpRequestHandler {
    Q_OBJECT
public:
    RequestMapper(QObject* parent=0);
    void service(HttpRequest& request, HttpResponse& response);
private:
    HelloWorldController helloWorldController;
    ListDataController listDataController;
    LoginController loginController;
};

#endif // REQUESTMAPPER_H

  修改requestmapper.cpp(切入了/login,调用loginController):

#include "requestmapper.h"

RequestMapper::RequestMapper(QObject* parent)
    : HttpRequestHandler(parent) {
    // empty
}

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 {
        response.setStatus(404,"Not found");
        response.write("The URL is wrong, no such document.");
    }

    qDebug("RequestMapper: finished request");
}

  运行程序并打开URLhttp://localhost:8080/login.将看到以下表格:

  在这里插入图片描述
  尝试使用错误的名称和密码登录。然后浏览器显示错误消息“That was wrong”,并提示重试。如果输入了正确的凭据(用户名“test”和密码“hello”),则会收到成功消息。

  HTML表单定义了两个名为“username”和“password”的输入字段。控制器使用request.getParameter()来获取这些值。

  当参数为空或传入的HTTP请求中没有这样的参数时,Request.getParameter() 返回一个空的QByteArray。后一种情况发生在打开URL时http://localhost:8080/login开始只有当用户单击提交按钮时,表单字段才会从web浏览器发送到web服务器。

  如果需要区分空字段和缺失字段,那么可以使用request.getParameterMap(),然后检查所需参数是否在返回的映射中。

  作为表单的替代方案,参数也可以作为URL的一部分进行传输。例如,也可以通过打开URL登录http://localhost:8080/login?username=test&password=hello.

  在URL中使用某些特殊字符时,必须将其编码为转义序列。例如,如果用户名是“Stefan Frings”,那么必须写http://localhost:8080/login?username=Stefan%20Frings&password=hello.HttpRequest类会自动将其解码回原始形式“Stefan Frings”。

  如果需要将字符串编码为URL格式,可以使用QUrl类。

服务器本地会话session

  (PS:session和cookie是一起搭配使用的,cookie存在本地 session可以拿到cookie来判断是否登录了,等一些已有的状态)
  下一个合乎逻辑的步骤是处理会话数据。这意味着,将当前用户的数据保存在某个地方,并在后续请求中使用这些数据。将存储的第一个数据是用户的姓名和登录时间。
  QtWebApp使用隐藏的cookie来识别用户。
  必须在控制会话存储类的配置文件webapp1.ini中添加一个新的部分:

[sessions]
expirationTime=3600000
cookieName=sessionid
;cookieDomain=mydomain.com
cookiePath=/
cookieComment=Identifies the user

  过期时间定义从内存中删除未使用的会话后的毫秒数。当用户在该时间之后返回时,他的会话将丢失,因此他必须再次登录。

  • expirationTime:保存时间,实际上从后面的cookie截图可以看到是3600000ms,则是3600秒一小时的时间过期,这个保存后的默认过期时间。
      在这里插入图片描述
  • sessionid:cookie名称可以是任意名称,但通常使用名称“sessionid”。有些负载均衡器依赖于这个名称,所以除非有充分的理由,否则不应该更改它。
  • cookieDomain:每个cookie总是绑定到一个域。由google.com生成的cookie只发送到该域上的服务器。如果将cookieDomain参数留空或将其注释掉,则该参数将由web浏览器自动设置。可以指定另一个域名,但不知道男人为什么要这么做。所以,除非知道自己在做什么,否则就把它空着。
  • cookiePath:cookie路径可用于将cookie限制在的域的一部分。如果将cookiePath更改为/my/very/cool/online/shop,则浏览器将仅针对以该路径开头的页面将cookie发送到的服务器。默认值为“/”,这意味着cookie对域中的所有网站都有效。
  • cookieComment:cookieComment是一些网络浏览器在cookie管理屏幕中显示的文本。
      需要HttpSessionStore类的一个实例,整个程序都可以访问该实例,因此可以在全局空间中访问。因此,创建了两个新文件,第一个是global.h:
#ifndef GLOBAL_H
#define GLOBAL_H

#include "httpsessionstore.h"

using namespace stefanfrings;

extern HttpSessionStore* sessionStore;

#endif // GLOBAL_H

  global.cpp:

#include "global.h"

HttpSessionStore* sessionStore;

  现在有了一个名为“sessionStore”的全局静态指针,整个程序可以通过包含global.h文件来访问该指针。让加载新的配置设置并初始化sessionStore。
  main.cpp中的更改:

#include "global.h"

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);

    // HTTP server
    QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,&app);
    listenerSettings->beginGroup("listener");
    new HttpListener(listenerSettings,new RequestMapper(&app),&app);

    return app.exec();
}

  请注意,main.cpp现在加载配置文件两次。sessionSettings对象选择“sessions”部分,而listenerSettings选择“listener”部分。对于每个部分,需要一个单独的QSettings实例,否则这些部分可能会混淆。
  既然已经为会话数据创建了一个存储,就可以开始使用它了。添加到logincontroller.cpp:

#include <QTime>
#include "logincontroller.h"
#include "global.h"

LoginController::LoginController(QObject* parent)
    :HttpRequestHandler(parent) {
    // empty
}

void LoginController::service(HttpRequest &request, HttpResponse &response) {
    HttpSession session=sessionStore->getSession(request,response,true);
    QByteArray username=request.getParameter("username");
    QByteArray password=request.getParameter("password");

    qDebug("username=%s",username.constData());
    qDebug("password=%s",password.constData());

    response.setHeader("Content-Type", "text/html; charset=UTF-8");
    response.write("<html><body>");

    if (session.contains("username")) {
        QByteArray username=session.get("username").toByteArray();
        QTime logintime=session.get("logintime").toTime();
        response.write("You are already logged in.<br>");
        response.write("Your name is: "+username+"<br>");
        response.write("You logged in at: "+logintime.toString("HH:mm:ss")+"<br>");
    }
    else {
        if (username=="test" and password=="hello") {
            response.write("Yes, correct");
            session.set("username",username);
            session.set("logintime",QTime::currentTime());
        }
        else {
            response.write("<form method='POST' action='/login'>");
            if (!username.isEmpty()) {
                response.write("No, that was wrong!<br><br>");
            }
            response.write("Please log in:<br>");
            response.write("Name:  <input type='text' name='username'><br>");
            response.write("Password: <input type='password' name='password'><br>");
            response.write("<input type='submit'>");
            response.write("</form");
        }
    }

    response.write("</body></html>",true);
}

  在这里,重要的是在第一次调用response.write()之前调用sessionStore->getSession(),因为它创建或刷新会话cookie,从技术上讲,它是一个HTTP头。并且所有HTTP标头都必须在HTML文档之前发送。
  通过打开来运行和测试程序http://localhost:8080/login.
  在这里插入图片描述
  现在查看web服务器的控制台窗口时,看到一个cookie和一个会话是用一个唯一的id创建的,这个id是一个长的随机十六进制数。
  从Chromium浏览器中截取了以下截图,可以在其中看到cookie:
  在这里插入图片描述
  用户会话最初为空。它只是存在,并且有一个唯一的id号。没有别的。输入用户名“test”和密码“hello”。然后会得到预期的确认。
  在这里插入图片描述
  在确认登录成功的同时,服务器将用户名和登录时间输入到用户会话中。可以将任何对象放入QVariant支持的会话中。当将一个对象放入会话时,会给它一个符号名称,以便以后访问。
  现在再次打开URLhttp://localhost:8080/login.然后会看到所有这些工作的结果:
  在这里插入图片描述
  因此,在成功验证用户名和密码后,服务器使用会话来记住该用户的数据。
  会话存储保存在内存中。重新启动web服务器时,会话存储中的所有数据都会丢失。因此,只将其用于一些临时数据。持久性数据属于一个数据库。

用户本地终端的存储cookies

  作为会话存储的替代方案,也可以在cookie中存储少量数据。Cookie存储在web浏览器的客户端,而不是服务器端。Cookie只能存储8位的文本,并且只能保证4 KB的容量。此外,每个域的cookie数量是有限的,因此请保留使用它们。
  为了尝试这一点,添加一个名为CookieTestController的新控制器类,并将其绑定到路径“/cookie”。

cookietestcontroller.cpp:

#include "cookietestcontroller.h"

CookieTestController::CookieTestController(QObject* parent)
    : HttpRequestHandler(parent) {
    // empty
}

void CookieTestController::service(HttpRequest &request, HttpResponse &response) {

    QByteArray cookie=request.getCookie("testcookie");
    if (!cookie.isEmpty()) {
        response.write("Found cookie with value: "+cookie,true);
    }
    else {
        HttpCookie cookie("testcookie","123456789",60);
        response.setCookie(cookie);
        response.write("A new cookie has been created.",true);
    }

}

  cookie的名称为“testcookie”,其值为“123456789”。60是以秒为单位的有效时间。 此cookie在创建一分钟后过期。这段短暂的时间来看看cookie过期后会发生什么。通常会使用更大的时间,可能是几天。
  Request.getCookie()只返回cookie的值,而不是整个HttpCookie对象。这是出于性能原因。
  运行程序并打开http://localhost:8080/cookie.
  在这里插入图片描述

  然后再次加载同一页面,将看到服务器正确接收到cookie:
  在这里插入图片描述

  Cookie存储在网络浏览器中,并随每个HTTP请求一起发送到网络服务器。除了会话数据之外,如果重新启动服务器,cookie不会丢失,因为cookie存储在客户端。
  Chromium浏览器的屏幕截图:
  在这里插入图片描述

  在这里,可以看到会话cookie仍然存在。现在有两个本地主机域的cookie。
  如果等待两分钟,然后再次加载http://localhost:8080/cookie将看到测试cookie已过期。服务器会创建一个新的。
  如果想防止cookie在用户处于活动状态时过期,那么必须在每次请求时重新创建cookie。然后,浏览器会为每个请求计算一个新的截止日期。


Demo增量:实战用户登录

步骤一:准备代码模板

  准备之前的demo v1.2.0模板:
  在这里插入图片描述

步骤二:新增登录入口处理LoginRequestHandler

  在这里插入图片描述

  修改一下:
  在这里插入图片描述

  在这里插入图片描述

步骤三:将登录界面的html的添加进登录入口

  在这里插入图片描述

void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response)
{

    QString str;

    QString path = request.getPath();
    LOG << path;

    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>"
          "    <a href=\"javascript:history.back(-1);\">返回上一页</a>"
          "    <form method='POST' action='/login'>"
          "        <p>Please login</p>"
          "        <p>Name: <input type='text' name='username'></p>"
          "        <p>Password: <input type='password' name='password'></p>"
          "        <p><input type='submit'></p>"
          "    </form>"
          "</body>";

    // 返回文本(需要在浏览器上看,所以将Qt内部编码都转成GBK输出即可,不管他本身是哪个编码)
//    QByteArray byteArray = _pTextCodec->fromUnicode(str);
    QByteArray byteArray = str.toUtf8();
    response.write(byteArray);
}

步骤四:在原来Index上新增一个连接地址跳转登录

  在这里插入图片描述
  在这里插入图片描述

  这个时候是可以跳转了。
  在这里插入图片描述
  此时,登录打印:
  在这里插入图片描述
  可以,其路径提交也是触发了一次进入login的service,这时候就需要通过Session的变量来判断是第一次进入还是提交登录。

步骤五:添加sessionStore头文件

  SessionStore头文件在httpserver模块中。
  在这里插入图片描述

步骤六:添加唯一实例类sessionStore,封装session存储类

  在这里插入图片描述
  在这里插入图片描述
  在这里插入图片描述

步骤七:添加配置文件

[sessions]
expirationTime=3600000
cookieName=sessionid
;cookieDomain=mydomain.com
cookiePath=/
cookieComment=Identifies the user

  在这里插入图片描述

步骤八:添加session加载配置代码

  在这里插入图片描述
  在这里插入图片描述

步骤九:通过session判断

  在没有session之前是可以获取表单提交的参数:
  在这里插入图片描述

  添加session判断后:

void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response)
{

    QString str;

    QString path = request.getPath();
    LOG << path;

    // 这里获取到的param是直接获取的
    QString userName = request.getParameter("username");
    QString password = request.getParameter("password");
    LOG << userName << password;

    // 获取session(单例模式封装后的全局获取)
    HttpSession httpSession =
            HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true);

    // 先判断是否session能获取到
    if(!httpSession.contains("username"))
    {
        // 这里没有cookie,提交登录则是可以保存本地cookie
        if(userName == "root" && password == "root123456")
        {
            httpSession.set("username" , userName);
            httpSession.set("logintime", QTime::currentTime());
            httpSession.set("timeout"  , 60);

            QString username = httpSession.get("username").toString();
            QTime logintime = httpSession.get("logintime").toTime();
            int timeout = httpSession.get("timeout").toInt();

            str = QString("<!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>"
                          "    <a href=\"javascript:history.back(-1);\">注销登录</a>"
                          "    <p>登录账户:%1</p>"
                          "    <p>登录时间:%2</p>"
                          "    <p>超时时间:%3</p>"
                          "</body>")
                    .arg(username)
                    .arg(logintime.toString("hh:mm:ss:zzz"))
                    .arg(timeout);

        }else{
            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>"
                  "    <a href=\"javascript:history.back(-1);\">返回上一页</a>"
                  "    <form method='POST' action='/login'>"
                  "        <p>Please login</p>"
                  "        <p>Name: <input type='text' name='username'></p>"
                  "        <p>Password: <input type='password' name='password'></p>"
                  "        <p><input type='submit'></p>"
                  "    </form>"
                "</body>";
        }
    }else {
        // 之前已经登录过了
        QString username = httpSession.get("username").toString();
        QTime logintime = httpSession.get("logintime").toTime();
        int timeout = httpSession.get("timeout").toInt();

        str = QString("<!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>"
                      "    <a href=\"javascript:history.back(-1);\">注销登录</a>"
                      "    <p>登录账户:%1</p>"
                      "    <p>登录时间:%2</p>"
                      "    <p>超时时间:%3</p>"
                      "</body>")
                .arg(username)
                .arg(logintime.toString("hh:mm:ss:zzz"))
                .arg(timeout);
    }

    // 返回文本(需要在浏览器上看,所以将Qt内部编码都转成GBK输出即可,不管他本身是哪个编码)
//    QByteArray byteArray = _pTextCodec->fromUnicode(str);
    QByteArray byteArray = str.toUtf8();
    response.write(byteArray);
}

  查看session
  在这里插入图片描述

步骤十:添加一个“注销登录”

  在这里插入图片描述

// 注销,清空session
if(path == "/login/out")
{
    HttpSession httpSession =
            HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true);

    HttpSessionStoreManager::getInstance()->getHttpSessionStore()->removeSession(httpSession);
}

Demo源码

LoginRequestHandler.h

#ifndef LOGINREQUESTHANDLER_H
#define LOGINREQUESTHANDLER_H

#include "httprequesthandler.h"

#include "HelloWorldRequestHandler.h"
#include "ListRequestHandler.h"

using namespace stefanfrings;

class LoginRequestHandler : public HttpRequestHandler
{
public:
    LoginRequestHandler(QObject *parent = 0);

public:
    void service(HttpRequest& request, HttpResponse& response);

private:
    QTextCodec *_pTextCodec;

private:

};
#endif // LoginRequestHandler_H

LoginRequestHandler.cpp

#include "LoginRequestHandler.h"

#include "ListRequestHandler.h"
#include "HttpSessionStoreManager.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;

LoginRequestHandler::LoginRequestHandler(QObject *parent)
    : HttpRequestHandler(parent)
{
    // 返回文本(需要在浏览器上看,所以将Qt内部编码都转成GBK输出即可,不管他本身是哪个编码)
    // WINDOWS: GBK  GB2312
    // LINUX  : urf-8
//    _pTextCodec = QTextCodec::codecForName("utf-8");
    _pTextCodec = QTextCodec::codecForName("GBK");
}

void LoginRequestHandler::service(HttpRequest &request, HttpResponse &response)
{

    QString str;

    QString path = request.getPath();
    LOG << path;

    // 这里获取到的param是直接获取的
    QString userName = request.getParameter("username");
    QString password = request.getParameter("password");
    LOG << userName << password;

    // 注销,清空session
    if(path == "/login/out")
    {
        HttpSession httpSession =
                HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true);

        HttpSessionStoreManager::getInstance()->getHttpSessionStore()->removeSession(httpSession);
    }

    // 获取session(单例模式封装后的全局获取)
    HttpSession httpSession =
            HttpSessionStoreManager::getInstance()->getHttpSessionStore()->getSession(request,response,true);

    // 先判断是否session能获取到
    if(!httpSession.contains("username"))
    {
        // 这里没有cookie,提交登录则是可以保存本地cookie
        if(userName == "root" && password == "root123456")
        {
            httpSession.set("username" , userName);
            httpSession.set("logindatetime", QDateTime::currentDateTime());
            httpSession.set("timeout"  , 60);

            QString username = httpSession.get("username").toString();
            QDateTime logindatetime = httpSession.get("logindatetime").toDateTime();
            int timeout = httpSession.get("timeout").toInt();

            str = QString("<!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>"
                          "    <a href=\"/login/out\"><input value='注销' type='reset'></a>"
                          "    <p>登录账户:%1</p>"
                          "    <p>登录时间:%2</p>"
                          "    <p>超时时间:%3</p>"
                          "</body>")
                    .arg(username)
                    .arg(logindatetime.toString("yyyy-MM-dd hh:mm:ss:zzz"))
                    .arg(timeout);

        }else{
            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>"
                  "    <a href=\"javascript:history.back(-1);\">返回上一页</a>"
                  "    <form method='POST' action='/login'>"
                  "        <p>Please login</p>"
                  "        <p>Name: <input type='text' name='username'></p>"
                  "        <p>Password: <input type='password' name='password'></p>"
                  "        <p><input type='submit'></p>"
                  "    </form>"
                "</body>";
        }
    }else {
        // 之前已经登录过了
        QString username = httpSession.get("username").toString();
        QDateTime logindatetime = httpSession.get("logindatetime").toDateTime();
        int timeout = httpSession.get("timeout").toInt();

        str = QString("<!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>"
                      "    <a href=\"/login/out\"><input value='注销' type='reset'></a>"
                      "    <p>登录账户:%1</p>"
                      "    <p>登录时间:%2</p>"
                      "    <p>超时时间:%3</p>"
                      "</body>")
                .arg(username)
                .arg(logindatetime.toString("yyyy-MM-dd hh:mm:ss:zzz"))
                .arg(timeout);
    }

    // 返回文本(需要在浏览器上看,所以将Qt内部编码都转成GBK输出即可,不管他本身是哪个编码)
//    QByteArray byteArray = _pTextCodec->fromUnicode(str);
    QByteArray byteArray = str.toUtf8();
    response.write(byteArray);
}

工程模板v1.3.0

  在这里插入图片描述


上一篇:《Qt+QtWebApp开发笔记(三):http服务器动态html连接跳转基础交互》
下一篇:敬请期待…


若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130921152

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/582145.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号量对象没有所有者

相较于互斥对象(Mutex)和临界区对象(Critical Section) &#xff0c;信号量没有所有者&#xff0c;它们只有计数。 ReleaseSemaphore 函数将会以指定的数量增加对应信号量对象的计数。 (增加计数这个动作&#xff0c;可能会释放正在等待的线程&#xff09;但是释放信号量的线程…

metaIPC2.0 SDK实现webRTC对讲IPC

概述 metaRTC新推出P2P版metaIPC2.0 sdk版本&#xff0c;基于mqtt通信&#xff0c;同时支持windows/linux/android操作系统&#xff0c;支持国内如海思/君正/瑞芯微/MSTAR等主流芯片。 metaIPC2.0支持linux/android/windows IPC&#xff0c;客户端支持浏览器/APP/windows和li…

【JavaSE】Java基础语法(二十七):Set集合和 TreeSet

文章目录 1. Set集合1.1Set集合概述和特点【应用】1.2Set集合的使用【应用】 2.TreeSet集合2.1TreeSet集合概述和特点【应用】2.2TreeSet集合基本使用【应用】2.3自然排序Comparable的使用【应用】2.4比较器排序Comparator的使用【应用】2.4两种比较方式总结 1. Set集合 1.1Se…

数据库必备知识:脏读和幻读的定义及应对策略

随着数据库应用的广泛使用&#xff0c;数据库并发性和一致性的问题成为了引起重视的问题之一。其中&#xff0c;脏读&#xff08;Dirty Read&#xff09;和幻读&#xff08;Phantom Read&#xff09;是常见的并发访问问题&#xff0c;本文将对脏读、幻读进行详细介绍&#xff0…

炸裂!GPT-4 开始自主进化,打造一个虚拟世界!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; 此前 GitHub 曾诞生过一个名为 Auto-GPT 的开源项目&#xff0c;让 AI 能够根据人类给出的目的&#xff0c;自动设定任务的优先级&#xff0c;尝试自行优化代码、自动改 Bug 等操作。不得不…

2024考研408-计算机组成原理第一章-计算机系统概述

文章目录 起步&#xff1a;认识计算机一、计算机发展历程1.1、什么是计算机系统1.2、计算机硬件的发展1.2.1、总览四代发展1.2.2、四代发展详细介绍1.2.3、计算机硬件的发展历史1.2.4、摩尔定律介绍 1.3、计算机软件的发展1.3.1、软件的发展1.3.2、目前的发展趋势 知识回顾与重…

Redis中的Reactor模型和执行命令源码探索

文章目录 摘要1、 了解Linux的epoll2、了解Reactor模型 一、Redis初始化1.1、配置初始化1.2、服务初始化1.3、网络监听初始化1.4、Reactor线程池初始化1.5、Reactor事件主循环 二、Reactor2.1、Reactor事件处理器2.2、读事件2.2.1、第一次读事件&#xff08;accept&#xff09;…

基于MATALB编程的深度信念网络DBN的01分类编码三分类预测,DBN算法详细原理

目录 背影 DBN神经网络的原理 DBN神经网络的定义 受限玻尔兹曼机(RBM) DBN的语音分类识别 基本结构 主要参数 数据 MATALB代码 结果图 展望 背影 DBN是一种深度学习神经网络,拥有提取特征,非监督学习的能力,是一种非常好的分类算法,本文将DBN算法对数据采用01编码分析…

【数据湖仓架构】数据湖和仓库:Azure Synapse 视角

是时候将数据分析迁移到云端了。我们将讨论 Azure Synapse 在数据湖和数据仓库范式规模上的定位。 在本文中&#xff0c;我们将讨论 Microsoft 的 Azure Synapse Analytics 框架。具体来说&#xff0c;我们关注如何在其中看到数据仓库和数据湖范式的区别。为了熟悉这个主题&…

【Linux】Linux环境基础工具的基本使用及配置(yum、vim)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 Linux软件包管理器 - y…

60分钟吃掉detectron2

本范例演示使用非常有名的目标检测框架detectron2 &#x1f917;&#x1f917; 在自己的数据集(balloon数据)上训练实例分割模型MaskRCNN的方法。 detectron2框架的设计有以下一些优点&#xff1a; 1&#xff0c;强大&#xff1a;提供了包括目标检测、实例分割、全景分割等非常…

Spring Boot启动流程

1 Springboot 启动流程 创建一个StopWatch实例&#xff0c;用来记录SpringBoot的启动时间。 通过SpringFactoriesLoader加载listeners&#xff1a;比如EventPublishingRunListener。 发布SprintBoot开始启动事件&#xff08;EventPublishingRunListener#starting()&#xff0…

性能测试——基本性能监控系统使用

这里写目录标题 一、基本性能监控系统组成二、环境搭建1、准备数据文件 type.db collectd.conf2、启动InfluxDB3、启动grafana4、启动collectd5、Grafana中配置数据源 一、基本性能监控系统组成 Collectd InfluxdDB Grafana Collectd 是一个守护(daemon)进程&#xff0c;用来…

【数据结构】时间复杂度与空间复杂度

目录 前言一、算法效率1. 算法效率的定义 二、时间复杂度1. 时间复杂度的定义2. 时间复杂度的计算 三、空间复杂度1. 空间复杂度的定义2. 空间复杂度的计算 四、时间复杂度曲线图结尾 前言 在学习C语言的时候&#xff0c;大多数的小伙伴们并不会对算法的效率了解&#xff0c;也…

视频采集到录制 - 音频采集到降噪

继续上篇的视频采集到录制 视频采集相对来说还是算正常&#xff0c;如果资源够用&#xff0c;使用第三方库也是种解决办法 但音频采集网上资料相对也少&#xff0c;走了一遍&#xff0c;也发现存在很多坑 1. 音频采集 一般来说&#xff0c;采用MIC采集&#xff0c;采集出来的格…

内存泄露的循环引用问题

内存泄漏一直是很多大型系统故障的根源&#xff0c;也是一个面试热点。那么在编程语言层面已经提供了内存回收机制&#xff0c;为什么还会产生内存泄漏呢&#xff1f; 这是因为应用的内存管理一直处于一个和应用程序执行并发的状态&#xff0c;如果应用程序申请内存的速度&…

希尔伯特旅馆里,住着AI的某种真相

“无穷”和“无穷1”&#xff0c;哪个更大&#xff1f; 已经吸收了不知道多少数据的AI模型&#xff0c;和比他多学习一条数据的模型&#xff0c;哪个更智能&#xff1f; 想聊聊这个问题&#xff0c;出于一个偶然的机会。很早之前我在测试ChatGPT的时候&#xff0c;突然想问他个…

简单工厂、工厂方法、抽象工厂模式-这仨货的区别

要想明白这三玩意的区别就需要知道这三玩意的优缺点&#xff1b; 之所以有三种工厂模式&#xff0c;就说明它们各有所长&#xff0c;能解决不同场景的问题&#xff1b; 一、简单工厂模式 UML图 代码 public class MobileFactory {public static Mobile getMobile(String brand)…

【Linux】线程概述、创建线程、终止线程

目录 线程概述1、创建线程函数解析代码举例 2、终止线程函数解析代码举例 橙色 线程概述 与进程类似&#xff0c;线程是允许应用程序并发执行多个任务的一种机制。一个进程可以包含多个线程。 进程是 CPU 分配资源的最小单位&#xff0c;线程是操作系统调度执行的最小单位。…

回归预测 | MATLAB实现SSA-CNN-LSTM麻雀算法优化卷积长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现SSA-CNN-LSTM麻雀算法优化卷积长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现SSA-CNN-LSTM麻雀算法优化卷积长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计学习总结参考资料 预测效果 基本介绍 MATLAB实现…