> 技术文档 > 【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务


📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 🏳️‍🌈一、增加请求后缀
    • 1.1 HttpRequest 类
    • 1.2 HttpHandler 类
  • 🏳️‍🌈二、状态码描述 及 自动跳转404
    • 2.1 状态码描述
    • 2.2 自动跳转404
  • 🏳️‍🌈三、重定向状态码
  • 🏳️‍🌈四、注册等多功能服务
    • 4.1 HttpRequest 类
    • 4.2 HttpHandler 类
    • 4.3 TcpServer.cpp
    • 4.4 测试
  • 👥总结

🏳️‍🌈一、增加请求后缀

我们在浏览器上访问我们自己的服务端时,会遇到客户端发送来的请求,想要访问 1.jpg 或者 default.html 因此我们可以将这个后缀给整理一下,通过日志打印告诉我们自己目标想要访问的资源在当前的哪里。

1.1 HttpRequest 类

我们在这个类里进行如下操作

  1. 添加成员变量 _suffix
  2. 在请求行解析方法中,增添一段,通过后缀分隔符 \".\" ,来找到我们的后缀,没有找到就返回默认后缀
  3. 添加函数方法,获取当前请求的后缀名
const static std::string _suffixsep = \".\";// 后缀分隔符 class HttpRequest {private: // 解析请求行 void PraseReqLine() { // 以空格为分隔符,不断读取 std::stringstream ss(_req_line); ss >> _method >> _url >> _version; _path += _url; // 处理url,如果是根目录,则返回默认路径 if (_url == \"/\") _path += _default_path; // 获取后缀 auto pos = _path.rfind(_suffixsep); if (pos == std::string::npos) _suffix = \".default\"; else _suffix = _path.substr(pos); }public: std::string Suffix() { LOG(LogLevel::INFO) << \"client want suffix : \" << _suffix; return _suffix; }}

1.2 HttpHandler 类

这里我们需要先知道一个概念 - MIMIE

MIME 是一种 ​互联网标准,最初设计用于扩展电子邮件协议(如 SMTP),使其能传输非文本数据(如图片、音频)。后被 HTTP 协议广泛采用,用于标识网络资源的 ​数据类型。

  • .html → text/html(HTML 文档)
  • .jpg → image/jpeg(JPEG 图片)
  • .json → application/json(JSON 数据)

这个类中我们需要增加一个后缀映射,并将其添加在返回报文的报头列表中

1, 增加成员变量 后缀后缀存储 的映射
2.`构造函数 时,初始化映射
3. 处理请求时,将映射结果添加到响应报头中

class HttpHandler {public: HttpHandler() { _mime_type.insert(std::make_pair(\".html\", \"text/html\")); _mime_type.insert(std::make_pair(\".jpg\", \"image/jpg\")); _mime_type.insert(std::make_pair(\".png\", \"image/png\")); _mime_type.insert(std::make_pair(\".default\", \"text/html\")); } std::string HandleRequest(std::string req) { std::cout << \"------------------------------------\" << std::endl; std::cout << req; HttpRequest req_obj; req_obj.Descrialize(req); std::string content = GetFileContent(req_obj.Path()); if (content.empty()) return std::string(); HttpResponse rsp; rsp.AddCode(200); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[req_obj.Suffix()]); rsp.AddBodyText(content); return rsp.Serialize(); }private: std::unordered_map _mime_type;};

🏳️‍🌈二、状态码描述 及 自动跳转404

前面的文章中我们已经知道了状态码描述的存在,但是没有可利用过,这里再介绍一下这个状态码,添加一个状态码的映射并且根据访问内容,去判断要不要显示404界面

2.1 状态码描述

这里需要进行两方面的更改,一个是 HttpResponse一个是 HttpHandler 的构造和成员变量

HttpHandler 中我们添加 状态码状态码描述映射,然后在构造中表示出来

class HttpHandler {public: HttpHandler() { _mime_type.insert(std::make_pair(\".html\", \"text/html\")); // HTML 类型 _mime_type.insert(std::make_pair(\".jpg\", \"image/jpeg\")); // JPEG 图片 _mime_type.insert(std::make_pair(\".png\", \"image/png\")); // PNG 图片 _mime_type.insert(std::make_pair(\".default\", \"text/html\")); // 默认文本类型 _status_code_desc.insert(std::make_pair(100, \"Continue\")); _status_code_desc.insert(std::make_pair(200, \"OK\")); _status_code_desc.insert(std::make_pair(201, \"Created\")); _status_code_desc.insert(std::make_pair(404, \"Not Found\")); }private: std::unordered_map _status_code_desc;};

HttpResponse 中的 AddCode 方法,我们之前默认是不论什么都是 OK,现在我们对其进行专属化处理

// 添加 状态码 和 状态码描述void AddCode(int code, std::string desc) { _status_code = code; _desc = desc;}

2.2 自动跳转404

我们在 HttpHandlerHandleRequest 方法中,当判定访问的路径内容为空时,就将这个路径改成 404.html

std::string HandleRequest(std::string req) { std::cout << \"------------------------------------\" << std::endl; std::cout << req; HttpRequest req_obj; req_obj.Descrialize(req); std::string content = GetFileContent(req_obj.Path()); HttpResponse rsp; if (content.empty()) { content = GetFileContent(\"wwwroot/404.html\"); rsp.AddCode(404, _status_code_desc[404]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[\".default\"]); rsp.AddBodyText(content); } else { rsp.AddCode(200, _status_code_desc[200]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[req_obj.Suffix()]); rsp.AddBodyText(content); } return rsp.Serialize();}

🏳️‍🌈三、重定向状态码

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

HTTP 状态码 301(永久重定向)和 302(临时重定向)都依赖 Location 选项。

无论是 HTTP 301 还是 HTTP 302 重定向,都需要依赖 Location 选项来指定资源的新位置。这个 Location 选项是一个标准的 HTTP 响应头部,用于告诉浏览器应该将请求重定向到哪个新的 URL 地址。

我们现在 default.html 中添加 测试重定向 的选项

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

我们测试永久重定向,将当前的网址重定向到 qq.com

std::string HandleRequest(std::string req) { std::cout << \"------------------------------------\" << std::endl; std::cout << req; HttpRequest req_obj; req_obj.Descrialize(req); HttpResponse rsp; if (req_obj.Url() == \"/redirect\") { // 重定向处理 std::string redirect_path = \"https://www.qq.com\"; rsp.AddCode(302, _status_code_desc[302]); rsp.AddHeader(\"Location\", redirect_path); rsp.AddHeader(\"Content-Type\", \"text/plain\"); // 添加 Content-Type rsp.AddHeader(\"Content-Length\", \"0\"); // 显式设置内容长度为 0 rsp.AddBodyText(\"\"); // 确保响应体为空 } else { std::string content = GetFileContent(req_obj.Path()); if (content.empty()) { content = GetFileContent(\"wwwroot/404.html\"); rsp.AddCode(404, _status_code_desc[404]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[\".default\"]); rsp.AddBodyText(content); } else { rsp.AddCode(200, _status_code_desc[200]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[req_obj.Suffix()]); rsp.AddBodyText(content); } } return rsp.Serialize();}

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

🏳️‍🌈四、注册等多功能服务

因为存在两种主要的提交方式,分别是 POSTGET,因此参数的位置会不一样,

GET 下,位于 网址 的 后面

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

POST 下,位于请求正文中

所以我们要根据不同的情况,分出响应的 参数,以及路径

4.1 HttpRequest 类

需要改的主要有三部分

  1. 增加新的成员变量_args 用来记录参数,_isexcute 用来记录是否有参数
  2. 构造函数中给 _isexcute 默认为false
  3. 更改 Descrialize 细节,使其区分 GETPOST,并且能够准确提取出 _args_path
class HttpRequest{ private: public: HttpRequest() : _blank_line(_base_sep), _path(_prefix_path), _isexcute(false) {} void Descrialize(std::string& reqstr){ // 基本的反序列化 _req_line = GetLine(reqstr); // 读取第一行请求行 // 请求报头 std::string header; do{  header = GetLine(reqstr);  // 如果既不是空,也不是空行,就是请求报头,加入到请求报头列表中  if(header.empty()) break;  else if(header == _base_sep) break;  _req_headers.push_back(header); }while(true); // 正文 if(!reqstr.empty())  _req_body = reqstr; // 进一步反序列化请求行 PraseReqLine(); // 分割请求报头,获取键值对 PraseHeader();  // 判断是否需要动态执行 if(_method == \"POST\"){  _isexcute = true;  _args = _req_body;  LOG(LogLevel::INFO) << \"POST _path : \" << _path;  LOG(LogLevel::INFO) << \"POST _args : \" << _args; } else if(_method == \"GET\"){  auto pos = _path.find(\"?\");  if(pos != std::string::npos){ _isexcute = true; _args = _path.substr(pos + 1); _path = _path.substr(0, pos); LOG(LogLevel::INFO) << \"GET _path : \" << _path; LOG(LogLevel::INFO) << \"GET _args : \" << _args;  } } } std::string Args(){ LOG(LogLevel::INFO) << \"client want _args : \" << _args;  return _args; } bool Isexecute(){ LOG(LogLevel::INFO) << \"client want _isexcute : \" << _isexcute;  return _isexcute; } private: bool _isexcute; // 是否需要动态执行 std::string _args; // 动态执行的参数 };

4.2 HttpHandler 类

增加功能路由表也就是映射目标路径和方法unoredered_map。同时也要构建能够处理相应操作的回调函数类型

using http_handler_t = std::function;std::unordered_map _route; // 功能路由表

增加注册服务的相关功能

// 注册服务功能 void RegisterHandler(std::string funcname, http_handler_t service) { std::string name = _prefix_path + funcname; _route.insert(std::make_pair(name, service)); } // 判断是否存在该功能 bool HasHandler(std::string funcname) { auto iter = _route.find(funcname); if (iter == _route.end()) return false; else return true; }

将http请求处理主要分为三块

  1. 重定向处理
  2. 动态操作处理
  3. 静态页面变化处理
std::string HandleRequest(std::string req) { std::cout << \"------------------------------------\" << std::endl; std::cout << req; HttpRequest req_obj; req_obj.Descrialize(req); HttpResponse rsp; if (req_obj.Url() == \"/redirect\") { LOG(LogLevel::DEBUG) << \"重定向服务\"; // 重定向处理 std::string redirect_path = \"https://www.qq.com\"; rsp.AddCode(302, _status_code_desc[302]); rsp.AddHeader(\"Location\", redirect_path); rsp.AddHeader(\"Content-Type\", \"text/plain\"); // 添加 Content-Type rsp.AddHeader(\"Content-Length\", \"0\"); // 显式设置内容长度为 0 rsp.AddBodyText(\"\"); // 确保响应体为空 } else if (req_obj.Isexecute()) { LOG(LogLevel::DEBUG) << \"注册服务\"; if (HasHandler(req_obj.Path())) { LOG(LogLevel::DEBUG) << \"找到注册服务\"; rsp = _route[req_obj.Path()](req_obj); } else { LOG(LogLevel::DEBUG) << \"没有找到注册服务\"; std::string content = GetFileContent(\"wwwroot/404.html\"); rsp.AddCode(404, _status_code_desc[404]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[\".default\"]); rsp.AddBodyText(content); } } else { LOG(LogLevel::DEBUG) << \"静态页面服务\"; std::string content = GetFileContent(req_obj.Path()); if (content.empty()) { content = GetFileContent(\"wwwroot/404.html\"); rsp.AddCode(404, _status_code_desc[404]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[\".default\"]); rsp.AddBodyText(content); } else { rsp.AddCode(200, _status_code_desc[200]); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-type\", _mime_type[req_obj.Suffix()]); rsp.AddBodyText(content); } } return rsp.Serialize();}

4.3 TcpServer.cpp

这里我们需要将注册服务具体化,并添加到 功能路由表

现实生活中,我们还需要对这个传进来的参数(用户名、密码等)进行序列化,到数据库中查找等操作,这里就不拓展了,简简单单地使用 success.html 界面表示我们登录成功就行了

HttpResponse Login(HttpRequest& req){ HttpResponse rsp; LOG(LogLevel::INFO) << \"进入登录模块\" << req.Path() << \", \" << req.Args(); std::string req_args = req.Args(); // 1. 解析参数格式,得到要的参数 // 2. 访问数据库,验证对应的用户是否是合法用户,以及... // 3. 登录成功 HttpHandler httphandler; std::string content = httphandler.GetFileContent(\"wwwroot/success.html\"); rsp.AddCode(200, \"OK\"); rsp.AddHeader(\"Content-Length\", std::to_string(content.size())); rsp.AddHeader(\"Content-Type\", \"text/html\"); rsp.AddHeader(\"Set-Cokkie\", \"req_args\"); rsp.AddBodyText(content); return rsp;}

4.4 测试

当 ·login 方法是 GET
【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

当是 POST
【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务

【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务


👥总结

本篇博文对 【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~