QT6 C++ 高仿QQ -- 5. 注册新用户实现,使用web框架drogon作为http api请求客户端

注册用户流程介绍

注册新用户涉及到用户重名检测,服务器防止DOS攻击的验证码请求,用户密码二次check,提交服务器进行用户注册等流程,下面用简要流程图表示
新用户注册流程

重名检查

用户注册采用http/https的api接口,使用drogon框架web开发框架,更多使用流程请查看drogon的相关文档,本文介绍http客户端请求相关流程。

drogon框架http客户端独立线程封装ServerApi

http客户端封装为独立线程,与mainwindow的ui直接用QT的信号与槽进行交互。

   //http客户端api调用独立线程
   class ServerApi : public QThread
    {
        Q_OBJECT
    public:
        explicit ServerApi(QObject* parent = Q_NULLPTR);
        ~ServerApi() = default;

        bool init(const std::string& userName, const std::string& password); //初始化数据库

        ChatMessagePtr findChatMessageById(const std::string& messageId);                               //根据ID同步查询某条消息, 在消息引用时需要
        bool updateChatMessageStatus(std::string messageId, proto::EMessageStatus status);  //更新某条聊天消息状态
        void deleteChatMessageById(const std::string& messageId);                                       //根据ID将消除从本地sqliet3数据库中删除

        std::deque<ChatMessagePtr> findFriendChatMessages(int64_t friendCode, int64_t createTime, bool group = false);      //查找某个好友的聊天数据
        Task<std::deque<ChatMessagePtr>> findFriendChatMessagesTask(int64_t friendCode, int64_t createTime, bool group = false);

        void stop();    //停止drogon 的http client线程
        void setAuthServer(std::string server);     //设置在线服务器

    public slots:
        void onRegisterUserRequested(QString userName, QString password, int64_t timestamp, QString requestId, QString imageCode, QString nickname);    //1. 用户注册
        void onCheckUserRequested(QString userName);																									//2. 重名检查
        void onImageCodeRequested(QString requestId, QString userName, IMAGE_CODE_TYPE source);																			//3. 图片验证码
        void onEmailCodeRequested(std::string requestId, std::string email, std::string imageCode, std::string imageCodeRequestId);                                     //5. 请求邮箱验证码
        void onFindUserRequested(std::string requestId, std::string email, std::string emailCode, std::string imageCode, std::string emailCodeRequestId, std::string imageCodeRequestId);

        void onResetPasswordRequested(std::string requestId, std::string userName, std::string email, std::string password, int64_t timestamp, std::string emailCode, std::string emailCodeRequestId,
            std::string imageCode, std::string imageCodeRequestId); //4. 请求 重置密码 http通道//6. 通过email找回用户

    signals:
        void registerUserResult(bool result, QString userName, QString password, qint64 timestamp, QString message);          //注册用户结果
        void checkUserResult(bool result);  //重名检查结果
        void imageCodeResultForChangePwd(bool result, QString requestId, QString base64Png, QString message);            //图形验证码 修改密码
        void imageCodeResultForUserRegister(bool result, QString requestId, QString base64Png, QString message);         //图形验证码 用户注册
        void imageCodeResultForFindPassword(bool result, QString requestId, QString base64Png, QString message);         //图形验证码 找回密码
        void emailCodeResult(bool result, std::string requestId, std::string email, std::string message);                       //email验证码 结果
        void findUserResult(bool result, std::string requestId, std::string email, std::string userName, std::string message);  //找回用户结果
        void resetPasswordResult(bool result, std::string requestId, std::string email, std::string userName, std::string message);     //重置密码结果
        void changePasswordResult(bool result, QString newPassword, int64_t timestamp);     //修改密码结果
    protected:
        void run();

    private:
        void handleImageCodeResult(bool result, QString requestId, QString base64Png, QString message, IMAGE_CODE_TYPE source); //验证码结果

        DbClientPtr dbClient_ = nullptr;

        std::string userName_;      //用户名
        QString password_;      //密码
        std::string server_;     //服务器信息

        QString userId_; //用户id
        QString token_;  //token

        DISALLOW_COPY_AND_ASSIGN(ServerApi);
    };

} // namespace client

api封装实现举例,1. drogon线程运行run;2. 重名检测;3. 用户注册请求

ServerApi::ServerApi(QObject* parent)
{}

//线程启动运行, drogon eventloop独立线程
void ServerApi::run()
{
    //Run HTTP framework,the method will block in the internal event loop
    trantor::Logger::setLogLevel(trantor::Logger::kInfo);
    std::cout << "trantor logger level: " << trantor::Logger::logLevel();
    qRegisterMetaType<ChatMessageDeque>("ChatMessageDeque&");
    app().run();
}

//停止drogon 的http client线程
void ServerApi::stop()
{
    app().quit();
}

/* 1.请求注册用户*/
void ServerApi::onRegisterUserRequested(QString userName, QString password, qint64 timestamp, QString requestId, QString imageCode, QString nickname)
{
    //参数错误
    if (userName.isEmpty() || password.isEmpty() || timestamp <= 0 || imageCode.isEmpty() || nickname.isEmpty())
        return;

    //http 请求的body中设置json的key value
    Json::Value data;
    data["requestId"] = requestId.toStdString();     //requestId
    data["imageCode"] = imageCode.toStdString();     //imagecode
    data["timestamp"] = timestamp;                   //加密用的时戳
    data["userName"] = userName.toStdString();      //用户名
    data["password"] = password.toStdString();      //密码
    data["nickname"] = nickname.toStdString();		//昵称

    qDebug("request register user, name: %s, password: %s, timestamp: %lld", userName.toStdString().c_str(), password.toStdString().c_str(), timestamp);

    //json to string
    std::string uri = "/api/users/register";

    //http请求参数设置
    auto req = HttpRequest::newHttpJsonRequest(data);
    req->setMethod(drogon::Post);
    req->setPath(uri);

    //http请求响应处理
    auto client = HttpClient::newHttpClient(server_);
    client->sendRequest(req, [this, uri, userName, password, timestamp](ReqResult result, const HttpResponsePtr& resp) {
        //获取请求结果失败
        if (result != ReqResult::Ok) {
            LOG(LS_ERROR) << "Failed to sendRequest uri: " << uri;
            Q_EMIT registerUserResult(false, userName, password, timestamp, QString::fromStdString("server return error json message"));
            return;
        }

        LOG(LS_INFO) << "Result: " << resp->getBody();
        //响应类型是否为json
        if (resp->contentType() != CT_APPLICATION_JSON) {
            LOG(LS_ERROR) << "uri: " << uri << ", response content type error";
            Q_EMIT registerUserResult(false, userName, password, timestamp, QString::fromStdString("response content type error"));
            return;
        }

        //解析json数据
        auto resultJson = resp->getJsonObject();
        if (!resultJson) {
            LOG(LS_ERROR) << "Error: uri: " << uri << ", Failed to get response json";
            Q_EMIT registerUserResult(false, userName, password, timestamp, QString::fromStdString("server return error json message"));
            return;
        }
        else {
            std::string code = (*resultJson)["code"].asString();
            std::string message = (*resultJson)["message"].asString();
            if (HTTP_RESPONSE_OK == std::stoi(code)) {
                //注册成功, 回传新密码 时戳信息保存到本地
                LOG(LS_INFO) << "Success: uri: " << uri;
                Q_EMIT registerUserResult(true, userName, password, timestamp, QString::fromStdString(message));
            }
            else {
                LOG(LS_INFO) << "uri: " << uri << ", response error code: " << code << ", message: " << message;
                Q_EMIT registerUserResult(false, userName, password, timestamp, QString::fromStdString(message));
            }
        }
        });
}

/* 2.注册用户的重名检查*/
void ServerApi::onCheckUserRequested(QString userName)
{
    //参数错误
    if (userName.isEmpty())
        return;

    //http 请求的body中设置json的key value
    Json::Value data;
    data["userName"] = userName.toStdString();     //要检查的用户名

    LOG(LS_INFO) << "request check username: " << userName.toStdString();

    //json to string
    std::string uri = "/api/users/nameCheck";

    //http请求参数设置
    auto req = HttpRequest::newHttpJsonRequest(data);
    req->setMethod(drogon::Post);
    req->setPath(uri);

    //http请求响应处理
    auto client = HttpClient::newHttpClient(server_);
    client->sendRequest(req, [this, uri, userName](ReqResult result, const HttpResponsePtr& resp) {
        //获取请求结果失败
        if (result != ReqResult::Ok) {
            LOG(LS_ERROR) << "Failed to sendRequest uri: " << uri;
            Q_EMIT checkUserResult(false); //检查结果 信号, 由 registerDialog 处理
            return;
        }

        LOG(LS_INFO) << "Result: " << resp->getBody();
        //响应类型是否为json
        if (resp->contentType() != CT_APPLICATION_JSON) {
            LOG(LS_ERROR) << "uri: " << uri << ", response content type error";
            Q_EMIT checkUserResult(false);
            return;
        }

        //解析服务器返回的json格式数据
       //解析json数据
        auto resultJson = resp->getJsonObject();
        if (!resultJson) {
            LOG(LS_ERROR) << "Error: uri: " << uri << ", Failed to get response json";
            Q_EMIT checkUserResult(false); //检查结果 信号, 由 registerDialog 处理
            return;
        }
        else {
            std::string code = (*resultJson)["code"].asString();
            std::string message = (*resultJson)["message"].asString();
            if (HTTP_RESPONSE_OK == std::stoi(code)) {
                //注册成功, 回传新密码 时戳信息保存到本地
                LOG(LS_INFO) << "Success: uri: " << uri;
                auto dataObj = (*resultJson)["data"];
                if (!dataObj) {
                    LOG(LS_ERROR) << "uri: " << uri << "Failed to parse data json obj";
                    Q_EMIT checkUserResult(false);
                    return;
                }

                bool checkResult = dataObj["exist"].asBool();
                if (true == checkResult) {

                    //注册成功, 回传新密码 时戳信息保存到本地
                    Q_EMIT checkUserResult(false);
                }
                else {

                    //注册成功, 回传新密码 时戳信息保存到本地
                    Q_EMIT checkUserResult(true);
                }
                qDebug() << "request check user name ok, result: " << checkResult;

            }
            else {
                LOG(LS_INFO) << "uri: " << uri << ", response error code: " << code << ", message: " << message;
                Q_EMIT checkUserResult(false); //检查结果 信号, 由 registerDialog 处理
            }
        }
        });
}

mainwindow中调用http的api接口

这里要处理的api接口比较多,本节介绍重名检查接口调用,其他接口调用使用类似。

    1. 在mainwindow中定义并创建server api独立线程的对象,启动drogon http客户端独立线程
 //http接口的 drogon 线程
 std::shared_ptr <ServerApi> serverApi_;  //drogon http/数据库api
 // 构造函数
 MainWindow::MainWindow(QWidget* parent) :
     QMainWindow(parent),
     ui(new Ui::MainWindow)
 {
     ui->setupUi(this);
     ......
     //drogon的http和数据库操作
     serverApi_ = std::make_shared<ServerApi>();   //auth server api
     serverApi_->start();
    ......
    1. 用户注册处理函数中链接重名检查,验证码,用户注册等信号与槽
//注册新用户
void MainWindow::onRegisterUser()
{
    RegisterDialog dialog;

    dialog.setWindowTitle(tr("New user"));
    connect(&dialog, &RegisterDialog::requestCheckName, serverApi_.get(), &ServerApi::onCheckUserRequested);              //1. 请求检查用户重名
    connect(serverApi_.get(), &ServerApi::checkUserResult, &dialog, &RegisterDialog::onCheckUserResult);                  //2. 收到 用户重名检查结果

    connect(&dialog, &RegisterDialog::requestImageCode, serverApi_.get(), &ServerApi::onImageCodeRequested);              //3. 图形验证码请求
    connect(serverApi_.get(), &ServerApi::imageCodeResultForUserRegister, &dialog, &RegisterDialog::onImageCodeResult);   //4. 收到 图形验证码 结果

    connect(&dialog, &RegisterDialog::requestRegisterUser, serverApi_.get(), &ServerApi::onRegisterUserRequested);        //5. 请求注册用户
    connect(serverApi_.get(), &ServerApi::registerUserResult, &dialog, &RegisterDialog::onRegisterUserResult);            //6. 注册用户结果

    //打开对话框
    if (dialog.exec() == QDialog::Accepted) {
        //将用户名 密码 加密用的时戳信息保存到本地
        userName_ = dialog.getUserName();
        password_ = dialog.getPassword();
        timestamp_ = dialog.timestamp();

        //发送消息给service
        if (connectServiceState_) {
            sendCredentialsRequest(true);
            //ui->ipcStateLabel->setText(userName_);
            //ui->ipcStateLabel->setStyleSheet("color:#CFCFCF;");
        }
    }
}

//检查用户重名结果
void RegisterDialog::onCheckUserResult(bool result)
{
    if (true != result) {
        userCheckResultLabel_->setText("NOK");
        userCheckResultLabel_->setStyleSheet("background: transparent; border-style:none; color: red");    //设置按钮 透明背景, 无边框, 字体颜色 蓝色
    }
    else {
        userCheckResultLabel_->setText("OK");
        userCheckResultLabel_->setStyleSheet("background: transparent; border-style:none; color: green");    //设置按钮 透明背景, 无边框, 字体颜色 蓝色
    }
}
/*
* 注册用户结果
* true: 注册成功 保存用户名 密码 时戳信息到本地的 QSetting 中
*/
void MainWindow::onRegisterUserResult(bool result, QString userName, QString password, int64_t timestamp, QString message)
{
    if (!result) {
        QMessageBox msg;
        msg.setText(message);
        msg.setStandardButtons(QMessageBox::Yes);
        msg.exec();
        QMessageBox::warning(this, tr("Register new user"), tr("Restart service first , login to server using new user") + message, QMessageBox::Ok);

        return;
    }

    //将用户名 密码 加密用的时戳信息保存到本地
    userName_ = userName;
    password_ = password;
    timestamp_ = timestamp;

    //发送消息给service
    if (connectServiceState_) {
        sendCredentialsRequest(true);
        //ui->ipcStateLabel->setText(userName_);
        //ui->ipcStateLabel->setStyleSheet("color:#CFCFCF;");

        QMessageBox::information(this, tr("Register new user"), tr("new user register ok, starting service..."), QMessageBox::Ok);
    }
    else {
        QMessageBox::warning(this, tr("Register new user"), tr("Restart service first , login to server using new user"), QMessageBox::Ok);
    }
}

点击注册按钮流程

    //确认按钮发起注册用户
    void RegisterDialog::onOk()    
    {
        userName_ = ui->userNameEdit->text();
        password_ = ui->passwordEdit->text();
        imageCode_ = ui->imageCodeEdit->text();
        QString confirmPwd = ui->confirmPasswordEdit->text();

        //用户名 密码不能为空
        if (userName_.isEmpty() || password_.isEmpty() || confirmPwd.isEmpty()) {
            QMessageBox msg;
            msg.setText(tr("User or password cannot be empty"));
            msg.setStandardButtons(QMessageBox::Yes);
            msg.exec();
            return;
        }

        if (userName_.size() < 4) {
            QMessageBox msg;
            msg.setText(tr("Cannot be less than 4 characters"));
            msg.setStandardButtons(QMessageBox::Yes);
            msg.exec();
            return;
        }

        //前后密码要一直
        if (password_ != confirmPwd) {
            QMessageBox msg;
            msg.setText(tr("Check your password again"));
            msg.setStandardButtons(QMessageBox::Yes);
            msg.exec();
            return;
        }

        if (imageCode_.isEmpty()) {
            QMessageBox msg;
            msg.setText(tr("Image code cannot be empty"));
            msg.setStandardButtons(QMessageBox::Yes);
            msg.exec();
            return;
        }

        if (ui->nicknameEdit->text().isEmpty()) {
            QMessageBox::warning(this, tr("Register a new user"), tr("nickname cannot be empty"), QMessageBox::Yes);
            return;
        }

        //aes加密后传输
        QDateTime currentTime = QDateTime::currentDateTime();
        int64_t timestamp = QDateTime::currentMSecsSinceEpoch();
        std::string encryptPassword;    //加密后的老密码

        bool ret = base::AesUtils::encryptPassword(userName_.toStdString(), password_.toStdString(), timestamp, encryptPassword);
        if (true != ret) {
            return;
        }

        //提交注册新用户信号
        emit requestRegisterUser(userName_, QString::fromStdString(encryptPassword), timestamp, requestId_, imageCode_, ui->nicknameEdit->text());
    }
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Drogon框架可以通过HTTP协议接收文件上传。以下是一个简单的示例代码,演示如何使用Drogon框架接收文件上传: ```cpp #include <drogon/HttpController.h> #include <drogon/HttpAppFramework.h> using namespace drogon; using namespace std; class FileUploadController : public drogon::HttpController<FileUploadController> { public: METHOD_LIST_BEGIN ADD_METHOD_TO(FileUploadController::upload, "/upload", HttpMethod::Post) METHOD_LIST_END void upload(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr&)>&& callback) { auto files = req->getUploadedFiles(); for (const auto& file : files) { LOG_DEBUG << "Uploaded file: " << file.getFileName(); // TODO: 保存上传的文件到服务器 } auto resp = HttpResponse::newHttpResponse(); resp->setContentTypeCode(CT_TEXT_HTML); resp->setBody("<html><body><h1>File uploaded successfully!</h1></body></html>"); callback(resp); } }; int main() { auto app = drogon::HttpAppFramework::instance(); app->addListener("0.0.0.0", 8080); app->registerController<FileUploadController>(); app->run(); return 0; } ``` 在以上示例代码中,我们使用了`getUploadedFiles()`方法获取上传的文件列表,然后可以对每个文件进行处理。注意,Drogon框架默认将上传的文件保存到内存中,如果上传的文件比较大,可能会导致内存占用过高。你可以使用`HttpRequest::getUploadFile(const std::string& filename, const std::string& tmpFilePrefix = "drogon")`方法将上传的文件保存到磁盘中,以避免内存占用过高的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chrisLee_sz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值