如何使用 boost/beast 解析并提取 HTTP POST 请求中的有效负载?

Rob*_*ong 5 c++ boost http-post boost-beast

我试图了解如何使用 Boost Beast 库处理 HTTP POST 请求的内容。我稍微修改了Boost Beast 高级服务器示例来掌握一些事情。

我已将以下几行添加到示例中的 handle_request() 方法中(就在第 155 行之前):

    if ( req.method() == http::verb::post)
    {
      std::cout << req << std::endl;
    }
Run Code Online (Sandbox Code Playgroud)

我创建了一个名为 foobar.dat 的简单测试文件,其中包含以下内容:

This is a test!
Run Code Online (Sandbox Code Playgroud)

我使用以下curl命令将其发送到服务器:

curl -F 'test=@foobar.dat' http://localhost:8080
Run Code Online (Sandbox Code Playgroud)

这会导致服务器输出以下内容:

POST / HTTP/1.1Host: localhost:8080
User-Agent: curl/7.58.0
Accept: */*
Content-Length: 218
Content-Type: multipart/form-data; boundary=------------------------9c747f078ebbe880

--------------------------9c747f078ebbe880
Content-Disposition: form-data; name="test"; filename="foobar.dat"
Content-Type: application/octet-stream

This is a test!

--------------------------9c747f078ebbe880--
Run Code Online (Sandbox Code Playgroud)

所以,我让服务器接收预期的消息。

如果我将测试稍微修改为以下内容,我可以单独提取标头字段以及一个大缓冲区中的正文。

    if ( req.method() == http::verb::post)
    {
      std::cout << "Fields:" << std::endl;

      int field_count = 0;
      for(auto const& field : req)
          std::cout << "Field#"
                    << ++field_count << " : " << field.name() << " = " << field.value() << std::endl;

      std::cout << "Body:" << std::endl;
      int body_count = 0;
      for(auto it = boost::asio::buffer_sequence_begin(req.body().data());
          it != boost::asio::buffer_sequence_end(req.body().data()); ++it)
      {
        // This is the next buffer in the sequence
        boost::asio::const_buffer const buffer = *it;

        std::string body(boost::asio::buffer_cast<const char*>(buffer));
        std::cout << "Buffer#" << ++body_count << " = " << body << std::endl;
      }
    }
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

Fields:
Field#1 : Host = localhost:8080
Field#2 : User-Agent = curl/7.58.0
Field#3 : Accept = */*
Field#4 : Content-Length = 218
Field#5 : Content-Type = multipart/form-data; boundary=------------------------5510ea3ec81b8585
Body:
Buffer#1 = --------------------------5510ea3ec81b8585
Content-Disposition: form-data; name="test"; filename="foobar.dat"
Content-Type: application/octet-stream

This is a test!

--------------------------5510ea3ec81b8585--
Run Code Online (Sandbox Code Playgroud)

我发现的大多数示例都演示了如何使用 Boost Beast 创建响应和请求,但我找不到任何明确的示例来说明如何解析内容并分离消息的各个组件。

具体来说,如何使用 Boost Beast 从正文中提取并分离名称(“test”)、文件名(“foobar.dat”)和文件内容(“这是一个测试!”),以便进行处理消息进一步?或者说,此时是否需要自己去解析消息体中的数据?

Tom*_*cky 2

这超出了 Boost::Beast 的范围。无论您寻求的信息是位于字段还是正文中,您都必须自己完成此操作。我建议使用一种方便的字符串操作工具/实用程序/库,例如 Boost::Algorithm::String 或 Abseil (absl::StrSplit)。

旁注:您可以像以前一样访问这些字段,也可以像这样直接访问这些字段

auto field = req["<field_name>"];
// or
auto it = req.find("<field_name>");
it->name_string();
Run Code Online (Sandbox Code Playgroud)

HTML 正文中的行由回车符和换行符分隔\r\n。正文由 double 组成\r\n。您可以通过编写精美的优化解析器或使用可用的实用程序之一来访问各个行。沿线分割,沿冒号分割,沿分号分割以访问namefilename,最后沿等号分割以获取数据。使用 Abseil 的示例,因为我不确定 boost 是否可以沿完整字符串(string_view)分割:

std::vector<std::string_view> lines = absl::StrSplit(req.body(), "\r\n");
std::vector<std::string_view> headers = absl::StrSplit(line, ':', absl::SkipWhitespace());
// or possibly (never tried it myself but abseil is a great library so I assume this should work)
std::array<std::string_view, 2> headers = absl::StrSplit(line, ':', absl::SkipWhitespace());
std::vector<std::string_view> items = absl::StrSplit(header, ';', absl::SkipWhitespace());
std::vector<std::string_view> values = absl::StrSplit(item, '=', absl::SkipWhitespace());
// or possibly again using array
Run Code Online (Sandbox Code Playgroud)