当前位置: 首页 > news >正文

【在线五子棋对战】十二、http请求处理

文章目录

  • http请求处理
    • 1、静态资源请求处理函数
    • 2、注册功能请求的处理函数
      • 后台处理
      • 前端处理
    • 3、登录功能请求的处理函数
      • 后台处理
      • 前端处理
    • 4、获取个人信息功能请求的处理函数
      • 后台处理
      • 前端处理

在这里插入图片描述

http请求处理

​ 对于 http 请求处理来说,主要分为四大类,有 页面请求注册请求登录请求用户信息获取请求,其中第一种请求属于 静态资源请求,后面三种是属于 动态功能请求,下面给出它们的框架:

    /*                 http请求处理函数                 */
private:// 静态资源请求处理函数void static_resource_handler(wsserver_t::connection_ptr& conn){}// 注册功能请求的处理函数void register_handler(wsserver_t::connection_ptr& conn){}// 登录功能请求的处理函数void login_handler(wsserver_t::connection_ptr& conn){}// 获取个人信息功能请求的处理函数void information_handler(wsserver_t::connection_ptr& conn){}
private:// 服务器收到http请求的处理函数,会根据请求类型的不同去调用上面四个函数来完成处理void http_callback(websocketpp::connection_hdl hdl){// 1. 获取请求的方法和uri,用于区分不同的请求类型wsserver_t::connection_ptr conn = _server.get_con_from_hdl(hdl);websocketpp::http::parser::request req = conn->get_request();std::string method = req.get_method();std::string uri = req.get_uri();// 2. 根据不同的请求类型调用对应的处理函数if(method == "POST" && uri == "/reg")          // 注册请求return register_handler(conn);else if(method == "POST" && uri == "/login")   // 登录请求return login_handler(conn);else if(method == "GET" && uri == "/info")     // 获取用户信息请求return information_handler(conn);elsereturn static_resource_handler(conn);      // 剩下的都认为是静态资源请求}

​ 其中 http_callback() 函数就是 websocket 服务器收到 http 请求的处理函数,我们会在这个函数内部根据请求类型的不同,选择要去调用哪个处理函数来处理请求!

​ 下面我们来实现它们!

1、静态资源请求处理函数

// 静态资源请求处理函数
void static_resource_handler(wsserver_t::connection_ptr& conn)
{// 1. 获取请求的uri资源路径websocketpp::http::parser::request req = conn->get_request();std::string uri = req.get_uri();// 2. 通过根目录和请求的uri组合成实际路径std::string path = _webroot + uri;// 3. 如果请求的是一个目录,那么就默认增加一个后缀login.htmlif(path.back() == '/')path += "index.html";// 4. 读取文件内容,并且判断是否读写成功std::string body;bool ret = file_util::read(path, body);if(ret == false){DLOG("静态资源请求异常,%s, 大小: %d", path.c_str(), body.size());// 读写失败则返回一个状态码为404的NOT FOUND页面body.clear();body += "<html>";body += "<head>";body += "<meta charset='UTF-8'/>";body += "</head>";body += "<body>";body += "<h1> Not Found </h1>";body += "</body>";body += "</html>";conn->set_status(websocketpp::http::status_code::not_found);conn->set_body(body);return;}// 5. 读写成功后设置响应正文,也就是将静态资源传回去conn->set_status(websocketpp::http::status_code::ok);conn->set_body(body);DLOG("静态资源请求成功, %s, 大小: %d", path.c_str(), body.size());
}

2、注册功能请求的处理函数

后台处理

​ 这段代码非常的冗余,对于响应的操作,基本都是一致的:

// 注册功能请求的处理函数
void register_handler(wsserver_t::connection_ptr& conn)
{// 1. 获取到请求正文 -- 为了获取用户名和密码std::string req_body = conn->get_request_body();// 2. 对正文进行反序列化,得到用户名和密码Json::Value body;bool ret = json_util::unserialize(req_body, body);if(ret == false){// 失败的话要返回错误响应DLOG("反序列化注册信息失败");Json::Value response;response["result"] = false;response["reason"] = "请求的正文格式错误";std::string response_body;json_util::serialize(response, response_body);conn->set_body(response_body);conn->set_status(websocketpp::http::status_code::bad_request);conn->append_header("Content-Type", "application/json");return;}// 3. 判断用户名和密码是否填写完整if(body["username"].isNull() || body["password"].isNull()){// 填写不完整的话要返回错误响应DLOG("用户名密码不完整");Json::Value response;response["result"] = false;response["reason"] = "请输入用户名/密码";std::string response_body;json_util::serialize(response, response_body);conn->set_body(response_body);conn->set_status(websocketpp::http::status_code::bad_request);conn->append_header("Content-Type", "application/json");return;}// 4. 进行数据库的用户信息操作 -- 如果成功状态码返回200,失败返回400ret = _user_table.sign_up(body);if(ret == false){// 写入用户信息错误的话要返回错误响应DLOG("向数据库插入数据失败");Json::Value response;response["result"] = false;response["reason"] = "该用户名已经被占用!";std::string response_body;json_util::serialize(response, response_body);conn->set_body(response_body);conn->set_status(websocketpp::http::status_code::bad_request);conn->append_header("Content-Type", "application/json");return;}Json::Value response;response["result"] = true;response["reason"] = "用户注册成功!";std::string response_body;json_util::serialize(response, response_body);conn->set_body(response_body);conn->set_status(websocketpp::http::status_code::bad_request);conn->append_header("Content-Type", "application/json");
}

​ 所以不妨我们再优化一下,将它们这些冗余部分提出来,用一个子函数 http_response() 来解决响应的问题,而我们要做的就是给这个子函数传递 通信句柄状态码result字段结果响应原因

​ 这样子的好处不仅仅是对注册函数有优化帮助,后面的几个 http 请求处理函数同样可以需要响应,所以同样能够通过调用 http_response() 减少冗余!

​ 下面给出优化后的代码:

// http响应处理通用函数
void http_response(wsserver_t::connection_ptr& conn, bool result, websocketpp::http::status_code::value status,const std::string& reason)
{// 1. 填写响应信息Json::Value response;response["result"] = result;response["reason"] = reason;// 2. 进行序列化std::string response_body;json_util::serialize(response, response_body);// 3. 设置http响应,类型为application/jsonconn->set_body(response_body);conn->set_status(status);conn->append_header("Content-Type", "application/json");
}// 注册功能请求的处理函数
void register_handler(wsserver_t::connection_ptr& conn)
{// 1. 获取到请求正文 -- 为了获取用户名和密码std::string req_body = conn->get_request_body();// 2. 对正文进行反序列化,得到用户名和密码Json::Value body;bool ret = json_util::unserialize(req_body, body);if(ret == false){// 反序列化失败的话要返回错误响应DLOG("反序列化注册信息失败");return http_response(conn, false, websocketpp::http::status_code::bad_request, "请求的正文格式错误");}// 3. 判断用户名和密码是否填写完整if(body["username"].isNull() || body["password"].isNull() || body["username"].asString().empty() || body["password"].asString().empty()){// 填写不完整的话要返回错误响应DLOG("用户名密码不完整");return http_response(conn, false, websocketpp::http::status_code::bad_request, "请输入正确的用户名和密码");}// 4. 进行数据库的用户信息操作 -- 如果成功状态码返回200,失败返回400ret = _user_table.sign_up(body);if(ret == false){// 写入用户信息错误的话要返回错误响应DLOG("向数据库插入数据失败");return http_response(conn, false, websocketpp::http::status_code::bad_request, "该用户名已经被占用!");}http_response(conn, true, websocketpp::http::status_code::ok, "用户注册成功");
}

前端处理

  • 给提交按钮添加点击事件 reg(),调用 javascript 写的注册函数
  • ret() 注册函数的实现
    1. 获取两个输入框中的数据,组织成一个 json
    2. 通过 ajax 向后台发送用户注册请求
      • 如果请求成功,则跳转到登录页面
      • 如果请求失败,则清空输入框内容,并提示错误原因

​ 下面代码中主要看脚本标签那里的代码,那才是处理和后台发送请求和响应的部分:

<!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>注册</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/login.css">
</head>
<body><div class="nav">网络五子棋对战游戏</div><div class="login-container"><!-- 登录界面的对话框 --><div class="login-dialog"><!-- 提示信息 --><h3>注册</h3><!-- 这个表示一行 --><div class="row"><span>用户名</span><input type="text" id="user_name" name="username"></div><!-- 这是另一行 --><div class="row"><span>密码</span><input type="password" id="password" name="password"></div><!-- 提交按钮 --><div class="row"><button id="submit" onclick="reg()">提交</button></div><div class="row"><button id="login" onclick="location.href='./login.html'">登录页面</button></div></div></div> <script src="js/jquery.min.js"></script><script>// 1. 给提交按钮添加点击事件function reg(){// 2. 获取两个输入框中的数据,组织成一个json串var reg_info = {username: document.getElementById("user_name").value,password: document.getElementById("password").value};// 3. 通过ajax向后台发送用户注册请求$.ajax ({url: "/reg",type: "post",data: JSON.stringify(reg_info),success: function(res){if(res.result == true){// 如果请求成功,则跳转到登录页面alert(res.reason)window.location.assign("/login.html");}else{// 如果请求失败,则清空输入框内容,并提示错误原因document.getElementById("user_name").value = "";document.getElementById("password").value = "";alert(res.reason);}},error: function(xhr){document.getElementById("user_name").value = "";document.getElementById("password").value = "";// alert(JSON.stringify(xhr))alert("请重新输入正确用户名和密码:可能用户名已存在或者用户名违例!")}})}</script>
</body>
</html>

3、登录功能请求的处理函数

后台处理

// 登录功能请求的处理函数
void login_handler(wsserver_t::connection_ptr& conn)
{// 1. 获取请求正文,进行反序列化,得到用户名和密码std::string req_body = conn->get_request_body();Json::Value body;bool ret = json_util::unserialize(req_body, body);if(ret == false){DLOG("反序列化登录信息失败");return http_response(conn, false, websocketpp::http::status_code::bad_request, "请求的正文格式错误");}// 2. 验证用户名和密码是否都填写if(body["username"].isNull() || body["password"].isNull() || body["username"].asString().empty() || body["password"].asString().empty()){DLOG("用户名密码不完整");return http_response(conn, false, websocketpp::http::status_code::bad_request, "请输入用户名和密码");}// 3. 进行数据库的用户信息验证,验证用户名和密码 -- 验证失败则返回400ret = _user_table.login(body);if(ret == false){DLOG("用户不存在或者密码错误");return http_response(conn, false, websocketpp::http::status_code::bad_request, "请输入正确的用户名和密码");}// 4. 如果验证成功,则给客户端创建session,并且设置过期时间session_ptr sp = _session.add_session(body["id"].asUInt64(), LOGIN);if(sp.get() == nullptr){// 创建会话失败的话返回的状态码是500DLOG("创建会话失败");return http_response(conn, false, websocketpp::http::status_code::internal_server_error, "创建会话失败");}_session.set_session_expire_time(sp->getSessionID(), SESSION_EXPIRE_TIME);// 5. 设置响应头部:Set-Cookie,将sessionID通过cookie响应返回conn->append_header("Set-Cookie", "SSID=" + std::to_string(sp->getSessionID()));http_response(conn, true, websocketpp::http::status_code::ok, "登录成功");
}

前端处理

​ 下面代码中主要看脚本标签那里的代码,那才是处理和后台发送请求和响应的部分:

<!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>登录</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/login.css">
</head>
<body><div class="nav">网络五子棋对战游戏</div><div class="login-container"><!-- 登录界面的对话框 --><div class="login-dialog"><h3>登录</h3><div class="row"><span>用户名</span><input type="text" id="user_name"></div><div class="row"><span>密码</span><input type="password" id="password"></div><div class="row"><button id="submit" onclick="login()">提交</button></div><div class="row"><button id="register" onclick="location.href='./register.html'">注册页面</button></div></div></div><script src="./js/jquery.min.js"></script><script>// 1. 给按钮添加点击事件,调用登录请求函数// 2. 封装登录请求函数function login(){// 1. 获取输入框中的用户名和密码,并组织json对象var login_info = {username: document.getElementById("user_name").value,password: document.getElementById("password").value};// 2. 通过ajax向后台发送登录请求$.ajax({url: "/login",type: "post",data: JSON.stringify(login_info),success: function(res){// 3. 如果验证通过,则跳转到游戏大厅页面alert("登录成功");window.location.assign("/game_hall.html");},error: function(xhr){// 4. 如果验证失败,则提示错误信息,并清空输入框alert("请输入正确的用户名和密码!");document.getElementById("user_name").value = "";document.getElementById("password").value = "";}})}</script>
</body>
</html>

4、获取个人信息功能请求的处理函数

后台处理

​ 其中因为要从 cookie 中分割出会话 id,所以我们封装出一个接口 get_sessionID_from_cookie() 提供此操作!

// 获取个人信息功能请求的处理函数
void information_handler(wsserver_t::connection_ptr& conn)
{// 1. 获取请求信息中的cookie信息std::string cookie_str = conn->get_request_header("Cookie");if(cookie_str.empty()){// 如果没有cookie信息,则返回错误,让客户端重新登录DLOG("找不到cookie信息,请重新登录");return http_response(conn, false, websocketpp::http::status_code::bad_request, "找不到cookie信息,请重新登录");}// 2. 从cookie中获取sessionID -- 封装一个接口并且调用std::string sessionID;bool ret = get_sessionID_from_cookie(cookie_str, "SSID", sessionID);if(ret == false){// cookie中没有会话id,则返回错误,让客户端重新登录DLOG("找不到会话id,请重新登录");return http_response(conn, false, websocketpp::http::status_code::bad_request, "找不到会话id,请重新登录");}// 3. 在session管理对象中查找对应的session信息session_ptr sp = _session.get_session_by_sesssionID(std::stol(sessionID));if(sp.get() == nullptr){// 在本地会话管理中找不到该会话,则认为登录已经过期,需要重新登录DLOG("会话过期,请重新登录");return http_response(conn, false, websocketpp::http::status_code::bad_request, "会话过期,请重新登录");}// 4. 先获取用户id,然后从数据库中取出用户信息uint64_t userid = sp->getUserID();Json::Value userinfo;ret = _user_table.select_by_id(userid, userinfo);if(ret == false){// 获取用户信息失败,返回错误:找不到用户信息DLOG("找不到用户信息,请重新登录");return http_response(conn, false, websocketpp::http::status_code::bad_request, "找不到用户信息,请重新登录");}// 5. 将用户信息进行序列化后发送给客户端 -- 一般不会序列化失败,所以这里不判断std::string body;json_util::serialize(userinfo, body);conn->set_body(body);conn->set_status(websocketpp::http::status_code::ok);conn->append_header("Content-Type", "application/json");// 6. 刷新session过期时间,也就是重新设置过期时间_session.set_session_expire_time(sp->getSessionID(), SESSION_EXPIRE_TIME);
}

前端处理

​ 下面代码中主要看脚本标签那里的代码,那才是处理和后台发送请求和响应的部分:

<!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>游戏大厅</title><link rel="stylesheet" href="./css/common.css"><link rel="stylesheet" href="./css/game_hall.css">
</head>
<body><div class="nav">网络五子棋对战游戏</div><!-- 整个页面的容器元素 --><div class="container"><!-- 这个 div 在 container 中是处于垂直水平居中这样的位置的 --><div><!-- 展示用户信息 --><div id="screen"></div><!-- 匹配按钮 --><div id="match-button">开始匹配</div></div></div><script src="./js/jquery.min.js"></script><script>function get_user_info() {$.ajax({url: "/info",type: "get",success: function(res) {var sl = 0;if(res.win_count != 0){ sl = parseInt(res.win_count * 100 / res.total_count); }var info_html = "<p>" + "用户: " + res.username + "  积分: " + res.score + "</br>" + "比赛场次: " + res.total_count + "  获胜场次: " + res.win_count + "  胜率: " + sl + "%" + "</p>";var screen_div = document.getElementById("screen");screen_div.innerHTML = info_html; },error: function(xhr) {alert(JSON.stringify(xhr));location.replace("/login.html");}})}get_user_info();</script>
</body>
</html>

在这里插入图片描述

http://www.lryc.cn/news/616268.html

相关文章:

  • 从 GPT‑2 到 gpt‑oss:解析架构的迭代
  • C++移动语义、完美转发及编译器优化零拷贝
  • win11(RTX5060)下进行nanodetplus训练
  • 2025年全国青少年信息素养大赛Scratch编程践挑战赛-小低组-初赛-模拟题
  • 动态工作流:目标结构源自表
  • 红楼梦文本数据分析
  • SpringBoot实现文件上传
  • CART算法:Gini指数
  • sqli-labs-master/Less-62~Less-65
  • 人工智能正在学习自我提升的方式
  • 《算法导论》第 17 章 - 摊还分析
  • 谷歌DeepMind发布Genie 3:通用型世界模型,可生成前所未有多样化的交互式虚拟环境
  • UE什么贴图要关闭SRGB
  • Virtio 驱动初始化数据收发流程详解
  • 太极行业观察:从传统技艺到数字化转型的演变|创客匠人
  • 【R studio数据分析】准备工作——下载安装
  • 【布局适配问题】响应式布局、移动端适配、大屏布局要点
  • 通过sealos工具在ubuntu 24.02上安装k8s集群
  • Loki+Alloy+Grafana构建轻量级的日志分析系统
  • FFmpeg实现音视频转码
  • Spring AOP 底层实现(面试重点难点)
  • AQS(AbstractQueuedSynchronizer)底层源码实现与设计思想
  • 前端路由:Hash 模式与 History 模式深度解析
  • Java Stream流详解:从基础语法到实战应用
  • Rust 实战四 | Traui2+Vue3+Rspack 开发桌面应用:通配符掩码计算器
  • 【算法题】:和为N的连续正数序列
  • 数学建模:控制预测类问题
  • Python 获取对象信息的所有方法
  • matlab实现随机森林算法
  • Doubletrouble靶机练习