Как вы используете boost / beast для анализа и извлечения полезной нагрузки из HTTP-запроса POST?

Я пытаюсь понять, как обрабатывать содержимое HTTP-запроса POST с помощью библиотеки Boost Beast. Я немного изменил Boost Beast advanced пример сервера, чтобы разобраться в этом.

Я добавил следующие строки в метод handle_request () в этом примере (непосредственно перед строкой 155):

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

Я создал простой тестовый файл foobar.dat, содержащий следующее:

This is a test!

Я отправляю его на сервер с помощью этой команды curl:

curl -F '[email protected]' http://localhost:8080

что приводит к следующему выводу с сервера:

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--

Итак, у меня есть сервер, получающий ожидаемое сообщение.

Если я немного изменю свой тест на следующее, я могу извлечь поля заголовка по отдельности, а также тело в один большой буфер.

    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;
      }
    }

Производит следующий вывод:

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--

Большинство примеров, которые я нашел, демонстрируют, как создавать ответы и запросы с помощью Boost Beast, но я не могу найти четких примеров того, что делать, чтобы проанализировать контент и разделить различные компоненты сообщения.

В частности, как мне использовать Boost Beast для извлечения и отделения имени («test»), имени файла («foobar.dat») и содержимого файла («Это тест!») От тела для обработки сообщение дальше? Или на этом этапе необходимо самостоятельно проанализировать данные в теле сообщения?


person Rob Armstrong    schedule 10.01.2020    source источник


Ответы (1)


Это выходит за рамки Boost :: Beast. Вам придется сделать это самостоятельно, независимо от того, находится ли информация, которую вы ищете, в полях или в теле. Я бы предложил один из удобных инструментов / утилит / библиотек для работы со строками, например Boost :: Algorithm :: String или Abseil (absl :: StrSplit).

Боковое примечание: вы можете получить доступ к полям, как вы, или прямо так

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

Строки в теле HTML разделяются символом возврата каретки и новой строки \r\n. Тело из заголовков двойным \r\n. Вы можете получить доступ к отдельным строкам, написав модный, оптимизированный парсер или используя одну из доступных утилит. Разделите по линиям, разделите по двоеточию, разделите по точке с запятой, чтобы получить доступ к name или filename, и, наконец, разделите по знаку равенства, чтобы получить данные. Пример использования Abseil, поскольку я не уверен, можно ли разбить ускорение по полной строке (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
person Tom Trebicky    schedule 08.05.2020