📢博客主页: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 类
我们在这个类里进行如下操作
- 添加成员变量
_suffix
- 在请求行解析方法中,增添一段,通过后缀分隔符
"."
,来找到我们的后缀,没有找到就返回默认后缀- 添加函数方法,获取当前请求的后缀名
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<std::string, std::string> _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<int, std::string> _status_code_desc;
};
HttpResponse 中的
AddCode
方法,我们之前默认是不论什么都是 OK,现在我们对其进行专属化处理
// 添加 状态码 和 状态码描述
void AddCode(int code, std::string desc) {_status_code = code;_desc = desc;
}
2.2 自动跳转404
我们在 HttpHandler
的 HandleRequest
方法中,当判定访问的路径内容为空时,就将这个路径改成 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();
}
🏳️🌈三、重定向状态码
HTTP 状态码 301(永久重定向)和 302(临时重定向)都依赖 Location 选项。
无论是 HTTP 301 还是 HTTP 302 重定向,都需要依赖 Location 选项来指定资源的新位置。这个 Location 选项是一个标准的 HTTP 响应头部,用于告诉浏览器应该将请求重定向到哪个新的 URL 地址。
我们现在
default.html
中添加测试重定向
的选项
我们测试永久重定向,将当前的网址重定向到
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-Typersp.AddHeader("Content-Length", "0"); // 显式设置内容长度为 0rsp.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();
}
🏳️🌈四、注册等多功能服务
因为存在两种主要的提交方式,分别是 POST
和 GET
,因此参数的位置会不一样,
GET
下,位于 网址 的?
后面
POST 下,位于请求正文中
所以我们要根据不同的情况,分出响应的 参数,以及路径
4.1 HttpRequest 类
需要改的主要有三部分
- 增加新的成员变量,
_args
用来记录参数,_isexcute
用来记录是否有参数 - 构造函数中给
_isexcute
默认为false - 更改
Descrialize
细节,使其区分GET
和POST
,并且能够准确提取出_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<HttpResponse(HttpRequest&)>;
std::unordered_map<std::string, http_handler_t> _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;elsereturn true;}
将http请求处理主要分为三块
- 重定向处理
- 动态操作处理
- 静态页面变化处理
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-Typersp.AddHeader("Content-Length", "0"); // 显式设置内容长度为 0rsp.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
时
当是 POST
时
👥总结
本篇博文对 【Linux网络】Http服务优化 - 增加请求后缀、状态码描述、重定向、自动跳转及注册多功能服务 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~