Consolexin's blog Consolexin's blog
首页
  • 算法基础

    • 图论
    • 字符串
    • 动态规划
    • 二分
    • 滑动窗口
    • 排序
  • Project

    • CppServer
  • 相关书籍

    • 现代C++编程
  • 书籍

    • SQL必知必会
    • MySQL必知必会
分类
标签
归档
GitHub (opens new window)

Consolexinhun

小学生
首页
  • 算法基础

    • 图论
    • 字符串
    • 动态规划
    • 二分
    • 滑动窗口
    • 排序
  • Project

    • CppServer
  • 相关书籍

    • 现代C++编程
  • 书籍

    • SQL必知必会
    • MySQL必知必会
分类
标签
归档
GitHub (opens new window)
  • README
  • day01-从一个最简单的socket开始
  • day02-不要放过任何一个错误
  • day03-高并发还得用epoll
  • day04-来看看我们的第一个类
  • day05-epoll高级用法-Channel登场
  • day06-服务器与事件驱动核心类登场
  • day07-为我们的服务器添加一个Acceptor
  • day08-一切皆是类,连TCP连接也不例外
  • day09-缓冲区-大作用
  • day10-加入线程池到服务器
  • day11-完善线程池,加入一个简单的测试程序
  • day12-将服务器改写为主从Reactor多线程模式
  • day13-支持业务逻辑自定义、完善Connection类
  • day14-重构核心库、使用智能指针
  • day15-重构Connection、修改生命周期
  • day16-使用CMake工程化
  • day17-使用EventLoopThreadPool、移交EventLoop
  • day18-HTTP有限状态转换机
  • day19-创建HTTP响应,实现HTTP服务器
  • day20-定时器的创建使用
  • day21-服务器主动关闭连接
  • day22-初步涉及日志库,定义自己的输出流LogStream
  • day23-定义前端日志库,实现同步输出
  • day24-异步日志库
  • day25-更有效的缓冲区
  • day26-监听写事件
  • day27-处理静态文件,实现POST请求
  • day28-文件服务器的简单实现,文件的展示和下载
  • day29-文件的上传
  • day30-WebBench的测试
  • CppServer
consolexinhun
2025-04-20

day27-处理静态文件,实现POST请求

# day27-处理静态文件,实现POST请求

在最外层对网络库的应用中,主要是对HTTP报文的各种处理,因此熟悉HTTP协议是进一步学习的关键。

在之前的http服务器中,服务器对客户端的返回直接定义在了c++文件中,但是在实际应用中,一般都需要编写相应的前端页面,然后直接返回页面。

在c++中,实现这种方法并不复杂,只需要将相应的文件读取保存为字符串,之后依照正常流程进行发送即可。

我们使用fstream标准库来处理读取文件,其他的方法也可以达到相同的目的,此处就不再赘述。

整体的读取文件的代码十分简单。只需要打开文件并读取即可。我们在http_server.cpp中定义了这个文件。


std::string ReadFile(const std::string& path){
    std::ifstream is(path.c_str(), std::ifstream::in);

    // 寻找文件末端
    is.seekg(0, is.end);

    // 获取长度
    int flength = is.tellg();

    //重新定位
    is.seekg(0, is.beg);
    char * buffer = new char[flength];

    // 读取文件
    is.read(buffer, flength);
    std::string msg(buffer, flength);
    return msg;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

随后,我们在设置回应时,先读取文件,然后在将作为回应体即可,需要注意的是,根据不同的数据类型,需要设定不同的ContentType

void HttpResponseCallback(const HttpRequest &request, HttpResponse *response)
{
    if(request.method() != HttpRequest::Method::kGet){
        response->SetStatusCode(HttpResponse::HttpStatusCode::k400BadRequest);
        response->SetStatusMessage("Bad Request");
        response->SetCloseConnection(true);
    }

    {
        std::string url = request.url();
        if(url == "/"){
            std::string body = ReadFile("../static/index.html");
            response->SetStatusCode(HttpResponse::HttpStatusCode::k200K);
            response->SetBody(body);
            response->SetContentType("text/html");
        }else if(url == "/mhw"){
            std::string body = ReadFile("../static/mhw.html");
            response->SetStatusCode(HttpResponse::HttpStatusCode::k200K);
            response->SetBody(body);
            response->SetContentType("text/html");
        }else if(url == "/cat.jpg"){
            std::string body = ReadFile("../static/cat.jpg");
            response->SetStatusCode(HttpResponse::HttpStatusCode::k200K);
            response->SetBody(body);
            response->SetContentType("image/jpeg");
        }else{
            response->SetStatusCode(HttpResponse::HttpStatusCode::k404NotFound);
            response->SetStatusMessage("Not Found");
            response->SetBody("Sorry Not Found\n");
            response->SetCloseConnection(true);
        }
    }
    return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

上述实现了简单的get请求,但是针对登陆注册等,使用get请求会将帐号密码暴露在url中,因此一般使用post请求。post和get请求的区别具体可以在网络搜索。在本教程中将简单的简述一下如何处理post请求。

在index.html中,有一个简单的登陆注册界面,通过点击登陆键将提交一个Content-Type为application/x-www-form-urlencoded的post请求,这种格式将提交一个类似URL中的key=value&...形式。此外还有application/json等此处并不再赘述,具体可以参见HTTP的各个头部字段。

因此,当我们接收到一个post请求时,依然需要首先判定它所请求的路径,针对不同的路径会进行不同的操作,由于本次只有/login的路径,因此只对其进行了设置,之后,开始分析请求体,对于一个登陆注册而言,其请求体是username=username&password=password这种形式的,因此对其解析即可。

if( request.method() == HttpRequest::Method::kPost){
    if(url == "/login"){
        // 进入登陆界面
        std::string rqbody = request.body();

        // 解析
        int usernamePos = rqbody.find("username=");
        int passwordPos = rqbody.find("password=");

        usernamePos += 9; // "username="的长度
        passwordPos += 9; // 

        // 找到中间分割符
        size_t usernameEndPos = rqbody.find('&', usernamePos);
        size_t passwordEndPos = rqbody.length();

        // Extract the username and password substrings
        std::string username = rqbody.substr(usernamePos, usernameEndPos - usernamePos);
        std::string password = rqbody.substr(passwordPos, passwordEndPos - passwordPos);

        // 简单的测试
        if (username == "wlgls"){
            response->SetBody("login ok!\n");
        }
        else{
            response->SetBody("error!\n");
        }
        response->SetStatusCode(HttpResponse::HttpStatusCode::k200K);
        response->SetStatusMessage("OK");
        response->SetContentType("text/plain");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

上述程序只是简单的应用,在现实中,还需要更加复杂的逻辑关系和代码,但是其整体的框架与这种形式是十分相似的,只需要进行内容的扩展即可。

编辑 (opens new window)
上次更新: 2025/05/21, 06:42:57
day26-监听写事件
day28-文件服务器的简单实现,文件的展示和下载

← day26-监听写事件 day28-文件服务器的简单实现,文件的展示和下载→

最近更新
01
6-其他操作
05-20
02
4-联结
05-20
03
7-管理
05-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Consolexinhun | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×