Chap08-Register
Chap08-Register
客户端
首先实现按下注册按钮的槽函数
void RegisterScreen::do_register_clicked()
{
auto res= doVerify();
if(!res){
return;
}
if (securityCode->text().trimmed().isEmpty()){
securityCode->setStyleSheet("border: 1px solid red;");
securityCode->setToolTip("此项不能为空");
}else{
securityCode->setStyleSheet("");
}
QJsonObject j;
j["user"] = accountEdit->text().trimmed();
j["password"] = passwordEdit->text().trimmed();
j["email"] = emailEdit->text().trimmed();
j["securityCode"] = securityCode->text().trimmed();
HttpManager::GetInstance()->PostHttp(QUrl(gate_url_prefix+"/userRegister"),
j, RequestType::REG_USER,Modules::REGISTERMOD);
}
先说一下,我们修改了doVerify函数,使得showTip在doVarify内部实现,而返回一个bool,然后我们根据这个bool值来决定是否进行接下来的操作。同时内部还对password和passwordSure进行验证,确认两次输入一致。
我们需要发送user,password,email,securityCode,交给HttpManager处理。
PostHttp处理之后会触发on_http_finished信号,槽函数do_http_finished根据RequestType决触发什么信号。
void HttpManager::do_http_finished(RequestType requestType, const QString &res, ErrorCodes errorCode, Modules mod)
{
switch(requestType)
{
case RequestType::GET_SECURITY_CODE:
emit on_get_code_finished(requestType,res,errorCode);
break;
case RequestType::REG_USER:
emit on_register_finished(requestType,res,errorCode);
break;
}
}
当有信号传进来之后,相应的槽函数处理结果
void RegisterScreen::do_register_finished(RequestType requestType,const QString&res,ErrorCodes errorCode)
{
qDebug() << static_cast<int>(errorCode);
qDebug() << static_cast<int>(ErrorCodes::SUCCESS);
if(errorCode!=ErrorCodes::SUCCESS){
showTip(0,tr("网络请求错误"));
return;
}
QJsonDocument jsonDoc = QJsonDocument::fromJson(res.toUtf8());
qDebug() << jsonDoc.toJson();
if(jsonDoc.isEmpty()){
showTip(0,"接收数据异常为空,无法解析");
return;
}
if(!jsonDoc.isObject()){
showTip(0,"接收数据异常,无法解析");
return;
}
_handlers[requestType](jsonDoc.object());
return;
}
后端
GateWayServer
首先在LogicSystem中注册对应路由的回调函数
RegistHandlers("/userRegister", RequestType::POST, [this](std::shared_ptr<Session> session) {
auto body_str = beast::buffers_to_string(session->_request.body().data());
std::cout << "receive userRegister request, body: " << body_str << std::endl;
session->_response.set(http::field::content_type, "text/json");
json j = json::parse(body_str);
if (j.is_null() || j.is_discarded()) {
std::cout << "无效json" << std::endl;
j["error"] = ErrorCodes::ERROR_JSON;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
}
std::string code;
bool get_code_success = RedisManager::GetInstance()->Get(EMAIL_PREFIX + j["email"].get<std::string>(), code);
if (!get_code_success) {
std::cout << "验证码获取失败" << std::endl;
j["error"] = ErrorCodes::ERROR_SECURITYCODE_EXPIRED;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
} else if (code != j["securityCode"].get<std::string>()) {
std::cout << "验证码错误" << std::endl;
j["error"] = ErrorCodes::ERROR_SECURITYCODE_NOTFOUND;
std::string str = j.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
}
// 先查找用户是否存在
// 这里简单测试注册功能
json jj;
jj["error"] = ErrorCodes::SUCCESS;
jj["email"] = j["email"].get<std::string>();
jj["user"] = j["user"].get<std::string>();
jj["password"] = j["password"].get<std::string>();
jj["securityCode"] = j["securityCode"].get<std::string>();
std::string str = jj.dump(4);
beast::ostream(session->_response.body()) << str;
return true;
});
先补充一下ErrorCodes的内容
enum class ErrorCodes {
SUCCESS = 0,
ERROR_NETWORK = 1001,
ERROR_JSON = 1002,
RPCFAILED = 1003,
ERROR_SECURITYCODE_EXPIRED = 1004,
ERROR_SECURITYCODE_NOTFOUND = 1005
};
这个枚举需要前后端协调一致。
回调函数的内容就是根据json解析的securityCode,然后与redis查询EMAIL_PREFIX+email的value值进行比较。
比如email=xxx@163.com
查询的是后就要查询"email_xxx@163.com"添加前缀用于区分
同理nodejs处理的时候,也要设置key为email_xxx@163.com
那么查询email_xxx@163.com得到对应的securityCode.
如果没有查到,就是ERROR_SECURITYCODE_NOTFOUND,否则查到不等的话就是ERROR_SECURITYCODE_EXPIRED(过期了)。
Nodejs with Redis
首先安装redis的库
npm install ioredis
编写redis.js,通过解析config.json的host,port,password,连接redis,定义操作。
const config_module = require('./config')
const Redis = require('ioredis');
const RedisCli = new Redis(
{
host:config_module.redis_host,
port:config_module.redis_port,
password:config_module.redis_passwd
});
RedisCli.on("error",function(error){
console.log("RedisCli connection error");
RedisCli.quit();
});
async function GetRedis(key){
try{
const result = await RedisCli.get(key);
if(result === null){
console.log("RedisCli key not found: "+key);
return null;
}
console.log("RedisCli get key: "+key+" value: "+result);
return result;
}catch(error){
console.log(error);
return null;
}
}
async function QueryRedis(key) {
try{
const result = await RedisCli.exists(key)
// 判断该值是否为空 如果为空返回null
if (result === 0) {
console.log('result:<','<'+result+'>','This key is null...');
return null
}
console.log('Result:','<'+result+'>','With this value!...');
return result
}catch(error){
console.log('QueryRedis error is', error);
return null
}
}
async function SetRedisExpire(key,value, exptime){
try{
// 设置键和值
await RedisCli.set(key,value)
// 设置过期时间(以秒为单位)
await RedisCli.expire(key, exptime);
return true;
}catch(error){
console.log('SetRedisExpire error is', error);
return false;
}
}
function Quit(){
RedisCli.quit();
}
module.exports = {GetRedis, QueryRedis, Quit, SetRedisExpire,}
那么server.js修改如下
async function GetSecurityCode(call, callback) {
console.log("Email is:", call.request.email)
try{
let redis_res = await redis_module.GetRedis(const_module.email_prefix+call.request.email);
let uniqueId = "";
if (redis_res == null){
uniqueId = uuidv4().toUpperCase().substring(0,4);
let bres = await redis_module.SetRedisExpire(const_module.email_prefix+call.request.email, uniqueId, 180);
if(!bres){
callback(null, { email: call.request.email,
error:const_module.Errors.RedisError
});
return;
}
}
else{
uniqueId = redis_res;
}
console.log("UniqueId is ", uniqueId)
let html_str = `...`
//发送邮件
let mailOptions = {
from: 'v125250@163.com',
to: call.request.email,
subject: '验证码',
html: html_str,
};
let send_res = await emailModule.SendMail(mailOptions);
console.log("Send Result Is ", send_res)
if (!send_res){
callback(null, { email: call.request.email,
error:const_module.Errors.Success
});
}
}catch(error){
console.log("Catch Error Is ", error)
callback(null, { email: call.request.email,
error:const_module.Errors.Exception
});
}
}
在这里,首先在redis数据库查询是否有对应的key/value.如果没有就生成验证码加入到数据库,并且设置过期时间为3分钟。如果有(这时候就是重复发请求的情况),就直接返回。

浙公网安备 33010602011771号