HTTP POST 请求不允许我定义上下文类型

HTTP POST request won't let me define context type

我正在使用 Ubuntu 18.04boost.asio 发送 POST 请求休息一下 API。当服务器收到请求时,它会捕获它,但我似乎无法定义它的内容类型。我有一个函数 collectRequestData 应该解析请求的主体,然后将其 return 保存到 MySQL 数据库中。当我打印函数 returns 时,它打印 null 而它应该是它发送的 JSON 文本。当我在调用函数之前打印 "Content-Type" 时,当我认为它应该是 "application/json" 时它打印未定义。我的最终目标是当我 运行 我的客户端代码 ./file.o localhost 8080 /licence '{JSON formatted text}' 它连接到本地主机端口 8080 路径 /licence (它正确执行)然后将 JSON 文本保存到 MySQL 数据库。它没有正确执行,我很确定原因是 "Content-Type" 我是使用服务器和 JavaScript 的新手,所以如果有人看到我做错了什么,请指出。另外,如果您能提供额外的细节以帮助我理解建议,我们将不胜感激。


下面是我发送 POST 请求的客户端代码

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;

int main(int argc, char* argv[])
{
    cout << "main -start" << endl;
    try
    {
        boost::asio::io_service io_service;
        string ipAddress = argv[1]; //"localhost" for loop back or ip address otherwise, i.e.- www.boost.org;       
        string portNum = argv[2]; //"8000" for instance;
        string hostAddress;
        if (portNum.compare("80") != 0) // add the ":" only if the port number is not 80 (proprietary port number).
        {
            hostAddress = ipAddress + ":" + portNum;
        }
        else 
        { 
            hostAddress = ipAddress;
        }
        string wordToQuery = "";//this will be used for entry indexing
        string queryStr = argv[3]; //"/api/v1/similar?word=" + wordToQuery;
        string json = argv[4];

        // Get a list of endpoints corresponding to the server name.
        tcp::resolver resolver(io_service);
        tcp::resolver::query query(ipAddress, portNum);
        tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

        // Try each endpoint until we successfully establish a connection.
        tcp::socket socket(io_service);
        boost::asio::connect(socket, endpoint_iterator);

        // Form the request. We specify the "Connection: close" header so that the
        // server will close the socket after transmitting the response. This will
        // allow us to treat all data up until the EOF as the content.
        string typeJSON = application/json;
        boost::asio::streambuf request;
        std::ostream request_stream(&request);
        request_stream << "POST " << queryStr << " HTTP/1.1\r\n";  // note that you can change it if you wish to HTTP/1.0
        request_stream << "Host: " << hostAddress << "\r\n";
        request_stream << "User-Agent: C/1.0";
        request_stream << "Content-Type: application/json; charset=utf-8\r\n";
        request_stream << "Accept: */*\r\n";
        request_stream << "Content-Length: " << json.length() << "\r\n"; 
        request_stream << "Connection: close\r\n\r\n";
        request_stream << json;

        // Send the request.
        boost::asio::write(socket, request);

        // Read the response status line. The response streambuf will automatically
        // grow to accommodate the entire line. The growth may be limited by passing
        // a maximum size to the streambuf constructor.
        boost::asio::streambuf response;
        boost::asio::read_until(socket, response, "\r\n");

        // Check that response is OK.
        std::istream response_stream(&response);
        std::string http_version;
        response_stream >> http_version;
        unsigned int status_code;
        response_stream >> status_code;
        std::string status_message;
        std::getline(response_stream, status_message);
        if (!response_stream || http_version.substr(0, 5) != "HTTP/")
        {
            std::cout << "Invalid response\n";
            return 1;
        }
        if (status_code != 200)
        {
            std::cout << "Response returned with status code " << status_code << "\n";
            return 1;
        }

        // Read the response headers, which are terminated by a blank line.
        boost::asio::read_until(socket, response, "\r\n\r\n");

        // Process the response headers.
        std::string header;
        while (std::getline(response_stream, header) && header != "\r")
        {
            std::cout << header << "\n";
        }

        std::cout << "\n";

        // Write whatever content we already have to output.
        if (response.size() > 0)
        {
            std::cout << &response;
        }

        // Read until EOF, writing data to output as we go.
        boost::system::error_code error;
        while (boost::asio::read(socket, response,boost::asio::transfer_at_least(1), error))
        {
            std::cout << &response;
        }

        if (error != boost::asio::error::eof)
        {
            throw boost::system::system_error(error);
        }
    }
    catch (std::exception& e)
    {
        std::cout << "Exception: " << e.what() << "\n";
    }

    return 0;
}

下面是我的服务器处理 POST 请求的部分

app.post('/licence', function (req, res) {
    collectRequestData(req, result => {
        //console.log(request.headers['Content-Type']);
        console.log(result);
        sleep(5000);
        connection.query('INSERT INTO licence SET ?', result, function (error, results) {
            if (error) throw error;
            res.end(JSON.stringify(results));
        });
        //res.end(`Parsed data belonging to ${result.fname}`);
    });
});

function collectRequestData(request, callback) {
    console.log(request.headers['Content-Type']);
    const FORM_URLENCODED = 'application/json';
    if(request.headers['Content-Type'] === FORM_URLENCODED) {
        let body = '';
        request.on('data', chunk => {
            body += chunk.toString();
        });
        request.on('end', () => {
            callback(JSON.parse(body));
        });
    }
    else {
        callback(null);
    }
}

您缺少行尾

request_stream << "User-Agent: C/1.0";

应该是

request_stream << "User-Agent: C/1.0\r\n";

表示您的内容类型 header 永远不会被识别,因为它不在单独的行上

我假设您正在为服务器使用 nodeJS

nodejs 给你小写的请求头

所以

function collectRequestData(request, callback) {
    console.log(request.headers['content-type']);
    const FORM_URLENCODED = 'application/json';
    if(request.headers['content-type'] === FORM_URLENCODED) {
        let body = '';

一旦您解决了@john 指出的其他问题,当然

You are missing an end of line

request_stream << "User-Agent: C/1.0";

should be

request_stream << "User-Agent: C/1.0\r\n";

感觉是网络服务器的问题,你可能不符合http协议规范。尝试使用 boost::beast,在 boost 1.66 及以上版本中可用。它是 boost::asio 的包装器,添加了高级网络服务器和网络套接字功能。您无需为低级 HTTP 实现而烦恼。

sample code

首先,我认为你应该仔细检查服务器是否正确。提供一种方法:

curl "http://localhost:8080/licence" --data "{json format text}" -v

如果此命令返回的结果与您的相同,则为服务器问题。 如果不行,请尝试assemble你代码中的请求包内容与curl相同,尤其是“\r\n”等。

正确的解决方案是@john 和@Jaromanda X 所说的组合,我还必须将 const FORM_URLENCODED = 'application/json'; 更改为 const FORM_URLENCODED = 'application/json; charset=utf-8'