主界面获取个人信息客户端方

主界面获取个人信息客户端方

前言

上一集我们完成了websocket身份验证的内容,那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。

需求分析

我们这边是完成客户端那方的内容,当客户端登录成功之后,我们就要从服务器获取到当前登录用户的基本信息。包括头像、昵称、id、电话、个性签名等。其中,我们的头像是要直接显示在我们的主界面上的。如下图:

对应proto文件

我们之前规定了这些前后端交互的接口的URL,所以我们就要去翻一下我们的proto文件。

我们先来看到我们的路径!!!

这就是我们的前后端交互接口的URL。

我们再来看到我们网络传输的请求和响应的proto文件。

这两个分别对应的是我们请求和响应的body。请求和响应我们都需要使用protobuf进行序列化

这里我们也是需要规定一下以下内容。

客户端和网关服务器进行通信的时候,需要填充sessionId属性,而不需要填充userId的属性,我们这里的这个规定是为了确认用户的身份信息,用于鉴权。

网关服务器和其他服务器进行通信的时候,我们就不需要填充sessionId属性,而一定要有userId属性。

getMyselfAsync

我们可以看到上面proto定义的内容,我们构造的请求里面需要有请求id,用户id以及会话id,这里我们需要传入DataCenter里保存的loginSessionId这个内容,请求id我们需要到NetClient中获取,这里的用户id我们是不需要传入的!

所以我们要在DataCenter当中添加以下方法

    //通过网络获取用户个人信息(异步)void getMyselfAsync();

看到这个函数的名称的末尾,我们就可以补充一个知识点:

同步与异步

async我们称之为异步,Sync称之为同步。

就举个例子解释一下。我们发送请求的时候,请求到返回一个响应的中间都是需要时间的。


我们的异步就是发送请求,就可以做其他的事情了,不会一直阻塞等待这个响应回来。

我们的同步就是与异步相反,我们发送请求后,我们需要阻塞去等这个响应回来接收到。


那么我们在代码中就不期望使用同步的方式来完成我们的功能,我们需要获取我们的个人信息的功能只要触发了,就能做其他事情去了,这个个人信息等他自己程序自己在另一个载体进行处理。


那么这个载体或者说接力棒,我们就可以很容易想到我们的信号槽。我们的程序的流程就是发送一个请求,把接力棒给到我们的信号槽,当响应回来了就触发我们的信号槽的信号,之后就针对响应的处理放到我们的槽函数。那样我们就不会在程序上卡住停滞不前了。


由于我们还需要进一步进行构造请求id,那么我们就要将loginSessionId这个参数带入到我们的NetClient,那么我们就继续创建一个方法在NetClient当中,接收我们的loginSessionId。那么我们先完成getMyselfAsync方法。

void DataCenter::getMyselfAsync()
{//这里注意!DataCenter只是处理数据,进行网络通信的是NetClientnetClient.getMyself(loginSessionId);
}

其实就是给NetClient传入loginSessionId这个信息。

NetClient::getMyself

那么进一步我们就要去完成网络通信部分的getMyself的方法。

我们的这个方法一共三个步骤

构造http的body部分、构造http请求,信号槽处理响应。

构造http的body

这一个部分是不同的请求需要构造的成员都不一样,所以我们就不能封装成一个函数来构造http的body,那么就只能按照proto文件一步一步来构造。

//构造http的body部分bite_im::GetUserInfoReq req;req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId);QByteArray body = req.serialize(&serializer);LOG() << "[获取个人信息] 发送请求 requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId;

这里我们需要body弄成二进制文件才能在网络上正常的传输。这个序列化器我们的NetClient里面也有,可以直接用上。

构造http请求

这一个部分就基本都是一个通用的操作了,我们就封装一个方法来实现吧。

我们先来看一下我们请求的一个格式

这个看上去还是十分的简单的。我们需要设置的就是我们是需要使用post方法,以及这个请求的路径,之后是content-type。当然我们最后要用一个QNetworkReply进行接收响应,并且返回这个响应的变量。

我们这里还是看到那个URL

由于我们的发送的不同的请求的内容只有URL和body是不同的,那么我们就要从getMyself那里传入我们需要发送的请求路径以及构造好的请求body即可。

QNetworkReply *NetClient::sendHttpRequest(const QString &apiPath, const QByteArray &body)
{QNetworkRequest httpReq;httpReq.setUrl(QUrl(HTTP_URL + apiPath));httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");QNetworkReply* httpResp = httpClient.post(httpReq,body);return httpResp;
}

信号槽处理响应

那么到这里,我们就已经把请求发送出去了,那么我们是要把接力棒交给我们的信号槽,我们就能让这个程序异步处理我们的请求了。

处理响应之前,我们要等到响应回来我们才能正式处理响应,所以我们的信号就是这个响应是finished的状态就可以开始我们的处理。

那么我们处理响应的时候是需要先判断我们的响应的业务上是否出错,HTTP上是否有出错。

如果我们的响应没有错误,我们就可以根据不同的响应做出不同的处理了。我们这里的需要把获取到的个人信息给我们保存到DataCenter当中,毕竟我们一开始在DataCenter里面我们的这个内容是nullptr的!


那么处理我们的业务上的逻辑是否出错我们也可以封装成一个方法。

但是这个函数需要能够处理不同的返回结果的对象,所以我们就要引入我们的模板,我们要去使用泛型编程。

返回值方面,我们虽然可以使用直接使用T来返回,但是!拷贝的开销是很大的。那么我们可以引入智能指针,在函数的内部,通过new的方式创建出对象,并返回指针,为了让这个对象能够在合适的时机进行释放,我们选用智能指针就是最好的办法!

那么判断这个业务逻辑是否有问题,我们就传入这个http的响应以及传入两个指针,一个ok用于判定是否有问题,一个reason用于接收错误原因。

 connect(httpResp, &QNetworkReply::finished, this, [=](){//返回值能获取一个反序列好的对象,并且判定业务上正确的bool ok = true;QString reason;auto resp = handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp, &ok, &reason);if(!ok){LOG() << "[获取个人信息] 出错!requestId=" << req.requestId() <<"reason=" << reason;return;}
}

那么我们就要进去完成我们的handleHttpResponse的内容。

handleHttpResponse

我们这个函数要先判断http是否出错,我们就要去看是否NoError

之后返回一个空的智能指针即可。要记得delete以下我们的响应对象。

之后就要获取我们响应的body,之后进行反序列化

    template <typename T>std::shared_ptr<T> handleHttpResponse(QNetworkReply* httpResp, bool* ok, QString* reason){//判定http是否出错if(httpResp->error() != QNetworkReply::NoError){*ok = false;*reason = httpResp->errorString();httpResp->deleteLater();return std::shared_ptr<T>();}//获取响应bodyQByteArray respBody = httpResp->readAll();//body反序列化std::shared_ptr<T> respObj = std::make_shared<T>();respObj->deserialize(&serializer, respBody);//判定业务上的逻辑if(!respObj->success()){*ok = false;*reason = respObj->errmsg();httpResp->deleteLater();return std::shared_ptr<T>();}//释放对象httpResp->deleteLater();return respObj;}

我们反序列化后能够得到以下的内容

我们就可以知道我们的业务逻辑是否是成功的,如果成功了我们就可以直接返回我们的respObj了,我们接收就可以在外面接收我们的respObj了。里面就包含了我们的userInfo!当然一定要记得我们的响应是已经使用完了,不需要他了,我们就要手动给他延迟delete!


C++模板

我们先不回到信号槽的代码!我们来谈一谈C++模板的问题

在C++中,模板的使用确实有一些特殊的考虑,特别是关于模板的声明和定义。你提到的“分离编译模型必须把声明和定义写在一起,不能分开写”的问题,实际上是由于C++模板的编译机制导致的。

模板的编译机制

C++模板是一种在编译时进行处理的泛型编程工具。编译器需要在编译时看到模板的完整定义,以便对模板进行实例化。这意味着,如果你只在头文件中声明模板而不提供定义,那么在其他.cpp文件中使用这个模板时,编译器将无法找到模板的定义,从而导致编译错误。

所以我们上面的那个函数就需要放到我们的头文件当中!


我们回到我们的信号槽部分,接下来我们就剩下把respObj里面的userInfo给放到我们的DataCenter当中。

也是十分的简单,请看下面的代码

我们一开始给myself就是一个nullptr,我们需要new一个新对象出来,再把userInfo给到我们的myself即可,这里也用到我们之前使用的load方法。

void DataCenter::resetMyself(std::shared_ptr<bite_im::GetUserInfoRsp> resp)
{if(myself == nullptr){myself = new UserInfo();}const bite_im::UserInfo& userInfo = resp->userInfo();myself->load(userInfo);
}

这里我们调用了这个函数就能把userInfo的内容放到我们的DataCenter里的myself了。


主窗口信号槽

我们只要进入主窗口就必须让这个头像和我们userInfo的内容就能够加载出来了,所以我们就要在整个触发连接信号槽的地方直接让他调用我们的getMyselfAsync的方法。只要一登录我们就能够从客户端发送请求到服务端获取我们的个人信息。

但是!展示在我们主页面的内容是也要显示正确才行啊,我们的头像还得渲染到我们的主页面的个人头像上,所以我们还得弄一个信号槽函数,用于接收一下信号,信号我们可以自定义一个,我们当把DataCenter的内容给弄上去之后就可以触发这个信号,信号被触发之后我们就会把头像的内容放到我们的主界面的userAvatar上。

    //获取个人信息connect(dataCenter, &DataCenter::getMyselfDone, this,[=](){//从DataCenter中拿到响应结果的myself,把里面的头像拿出来,放置在界面上auto myself = dataCenter->getMyself();userAvatar->setIcon(myself->avatar);});dataCenter->getMyselfAsync();
signals://自定义信号void getMyselfDone();
//通过网络获取用户个人信息
void NetClient::getMyself(const QString &loginSessionId)
{//构造http的body部分bite_im::GetUserInfoReq req;req.setRequestId(makeRequestId()); req.setSessionId(loginSessionId);QByteArray body = req.serialize(&serializer);LOG() << "[获取个人信息] 发送请求 requestId=" << req.requestId() << ", loginSessionId=" << loginSessionId;//构造http请求QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body);//通过信号槽,获取到当前的响应connect(httpResp, &QNetworkReply::finished, this, [=](){//返回值能获取一个反序列好的对象,并且判定业务上正确的bool ok = true;QString reason;auto resp = handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp, &ok, &reason);if(!ok){LOG() << "[获取个人信息] 出错!requestId=" << req.requestId() <<"reason=" << reason;return;}//响应数据保存到DataCenter中,一开始个人信息是nullptr的dataCenter->resetMyself(resp);//通知调用逻辑,响应处理完毕emit dataCenter->getMyselfDone();//打印日志LOG() << "[获取个人信息] 响应处理完毕!requestId=" << req.requestId();});}

我们程序的链路就是:

  1. 主窗口调用DataCenter的getMyselfAsync
  2. 把DataCenter的loginSessionId传给NetClient,调用getMyself
  3. 进入getMyself,发送请求,返回并处理请求,保存myself,触发信号
  4. 触发信号,主窗口渲染头像即可。

这样我们客户端这边就暂时告一段落了,可以准备去完成测试服务器那边的内容。

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

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

相关文章

Spring整合Redis

前言 在Spring项目中整合Redis&#xff0c;能显著提升数据缓存、分布式锁、会话管理等操作的效率。Jedis作为轻量级的Java Redis客户端&#xff0c;搭配Spring Data Redis模块&#xff0c;能够简化Redis的连接和数据操作&#xff0c;实现更高性能的读写与灵活的缓存管理。本文…

爬虫——Requests库的使用

在爬虫开发中&#xff0c;HTTP请求是与服务器进行交互的关键操作。通过发送HTTP请求&#xff0c;爬虫可以获取目标网页或接口的数据&#xff0c;而有效地处理请求和响应是爬虫能够高效且稳定运行的基础。Requests库作为Python中最常用的HTTP请求库&#xff0c;因其简洁、易用和…

深入描述dts和dtsi的区别

QA&#xff1a;dts和dtsi的区别 在嵌入式 Linux 系统中&#xff0c;DTS&#xff08;Device Tree Source&#xff09;和 DTSI&#xff08;Device Tree Source Include&#xff09;是描述硬件设备树的文件格式。它们本质上是同一种语法的文件&#xff0c;但在使用上有一定区别。…

LinkedHashMap实现LRU

LRU 环境&#xff1a;JDK11 最近接触LRU(Least Recently Used)&#xff0c;即最近最少使用&#xff0c;也称淘汰算法&#xff0c;在JDK中LinkedHashMap有相关实现 LRU的LinkedHashMap实现 LinkedHashMap继承HashMap。所以内存的存储结构和HashMap一样&#xff0c;但是LinkedH…

IDEA部署AI代写插件

前言 Hello大家好&#xff0c;当下是AI盛行的时代&#xff0c;好多好多东西在AI大模型的趋势下都变得非常的简单。 比如之前想画一幅风景画得先去采风&#xff0c;然后写实什么的&#xff0c;现在你只需描述出你想要的效果AI就能够根据你的描述在几分钟之内画出一幅你想要的风景…

27-压力测试

测试目标 & 测试数据 ● 测试目标 ○ 测试集群的读写性能 / 做集群容量规划 ○ 对 ES 配置参数进行修改&#xff0c;评估优化效果 ○ 修改 Mapping 和 Setting&#xff0c;对数据建模进行优化&#xff0c;并测试评估性能改进 ○ 测试 ES 新版本&#xff0c;结合实际场…

正则表达式常用字符

基础正则 ^:开头字符 $:结尾字符 ^$:空行 .:任意一个字符 *:前一个字符连续出现0次或以上 .*:所有 []&#xff1a;括号中的任意一个字符 [a-z] [a-zA-Z0-9] [a-zA-Z0-9] [^]:除括号内以外的字符 扩展正则 |:或 ssh|telnet|http ():表示整体 ^(ssh|telnet|http)^ssh|^telnet|^ht…

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明 文章目录 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明前言1. Ribbon 介绍1.1 LB(Load Balance 负载均衡) 2. Ribbon 原理2.2 Ribbon 机制 3. Spring Cloud Ribbon 实现负载均衡算法-应用实例4. 总结&#x…

Redis 线程模型详解:理解 Redis 高效性能的关键

Redis 是一个开源的高性能键值存储系统&#xff0c;因其卓越的速度和强大的功能被广泛应用于各种场景&#xff0c;如缓存、消息队列和实时数据存储等。Redis 的性能优越不仅归功于其高效的数据结构和内存存储&#xff0c;还源于其独特的线程模型。本文将详细介绍 Redis 的线程模…

vue3【实战】切换全屏【组件封装】FullScreen.vue

效果预览 原理解析 使用 vueUse 里的 useFullscreen() 实现 代码实现 技术方案 vue3 vite UnoCSS vueUse 组件封装 src/components/FullScreen.vue <template><component:is"tag"click"toggle":class"[!isFullscreen ? i-ep:full-sc…

docker:基于Dockerfile镜像制作完整案例

目录 摘要目录结构介绍起始目录package目录target目录sh目录init.sh脚本start.sh脚本stop.sh脚本restart.sh脚本 config目录 步骤1、编写dockerfilescript.sh脚本 2、构件镜像查看镜像 3、保存镜像到本地服务器4、复制镜像文件到指定目录&#xff0c;并执行init.sh脚本5、查看挂…

lua实现雪花算法

lua实现雪花算法 雪花算法介绍组成部分优点缺点 代码示例 雪花算法介绍 雪花算法&#xff08;Snowflake Algorithm&#xff09;是一种用于生成唯一ID的分布式生成算法&#xff0c;最初由Twitter开发。它的主要目的是在分布式系统中生成唯一的、时间有序的ID&#xff0c;这些ID通…

Spring Boot之Spring-devtools热部署

1、导包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope> </dependency>2、添加配置 #开启热部署 spring.devtools.restart.enabledtrue #热…

STM32 | 超声波避障小车

超声波避障小车 一、项目背题 由于超声波测距是一种非接触检测技术&#xff0c;不受光线、被测对象颜色等的影响&#xff0c;较其它仪器更卫生&#xff0c;更耐潮湿、粉尘、高温、腐蚀气体等恶劣环境&#xff0c;具有少维护、不污染、高可靠、长寿命等特点。因此可广泛应用于…

第6章:TDengine 标签索引和删除数据

TDengine 标签索引和删除数据 目标 掌握标签索引的创建、删除掌握超表、子表创建以及数据删除删除数据 删除数据是 TDengine 提供的根据指定时间段删除指定表或超级表中数据记录的功能,方便用户清理由于设备故障等原因产生的异常数据。 注意:删除数据并不会立即释放该表所…

微澜:用 OceanBase 搭建基于知识图谱的实时资讯流的应用实践

本文作者&#xff1a; 北京深鉴智源科技有限公司架构师 郑荣凯 本文整理自北京深鉴智源科技有限公司架构师郑荣凯&#xff0c;在《深入浅出 OceanBase 第四期》的分享。 知识图谱是一项综合性的系统工程&#xff0c;需要在在各种应用场景中向用户展示经过分页的一度关系。 微…

多轮对话中让AI保持长期记忆的8种优化方式篇

多轮对话中让AI保持长期记忆的8种优化方式篇 一、前言 在基于大模型的 Agent 中&#xff0c;长期记忆的状态维护至关重要&#xff0c;在 OpenAI AI 应用研究主管 Lilian Weng 的博客《基于大模型的 Agent 构成》[1]中&#xff0c;将记忆视为关键的组件之一&#xff0c;下面我…

消息中间件分类

消息中间件&#xff08;Message Middleware&#xff09;是一种在分布式系统中实现跨平台、跨应用通信的软件架构。它基于消息传递机制&#xff0c;允许不同系统、不同编程语言的应用之间进行异步通信。 常见的消息中间件类型包括&#xff1a; 1. JMS&#xff08;Java Message S…

aws-athena查询语句总结

完全归于本人mysql语句小白&#xff0c;是一点也写不出来&#xff0c;故汇总到此 1. cloudtrail ## 查询事件排序 SELECT eventname,eventtime,count(eventname) as num FROM your_athena_tablename where eventtime between 2024-11-10 and 2024-11-11 group by eventname…

Swift的可选绑定(Optional binding)

在Swift中&#xff0c;有一种变量称为可选变量&#xff08;Optional&#xff09;&#xff0c;具体说明见Swift初步入门。这种变量的值可以存在也可以为空&#xff08;nil&#xff09;。在Swift中&#xff0c;可以通过将if语句和赋值语句结合&#xff0c;有条件地展开&#xff0…