Curl 与 CPPREST

Curl vs CPPREST

我正在尝试使用 CPPREST http_client 访问 URL:

http://www.20min.ch/rss/rss.tmpl?type=channel&get=68

我收到 URL- 重定向的响应代码 302。

但是当我尝试使用 CURL 访问相同的 URL 时,我收到 CURLE_OK。

下面是 2 段代码:

using CURL :

CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if(curl){
    curl_easy_setopt(curl, CURLOPT_URL, "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68");
    res = curl_easy_perform(curl);
    if(res != CURLE_OK)     {
        cout<<"failed";
    }
    else  {
        cout<<"success";
    }
    curl_easy_cleanup(curl);
}
curl_global_cleanup();

输出为:成功

using CPPREST :

std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
try
{
     http_client client1(U(url_));
     uri_builder builder1(U(""));
     client1.request(methods::GET, builder1.to_string()).then([=](http_response response)
     {
        cout<<"Response code is : "<<response.status_code();
     });
}
catch(std::exception& e)
{
    cout<<"response :"<<e.what();
}

输出为 :: 响应代码为:302

我不明白为什么两个库的行为不同 URL??

更新:

我也试过:

http_client client1(utility::conversions::to_string_t(url_));

http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));

http_client client1(U("http://www.20min.ch/"));

但响应与 cpp rest 相同的 302。 [ 用于交叉检查 bing example

工作正常]

更新 2:

@Matt Weber 解释的方法似乎非常有用且合法,但我不断收到错误代码:400,所以我尝试了以下方法: 我试图在 uri_builder.

中为 URL 设置主机和端口
http_client client(U("http://www.20min.ch/rss/"));
uri_builder builder(U("/rss.tmpl"));
builder.append_query(U("type"), U("channel"));
builder.append_query(U("get"), U("68"));
builder.set_host(U("www.20min.ch"));
builder.set_port(U("80"));
client.request(methods::GET, builder.to_string()).then([=](http_response response)
{
     cout<<"Received response status code: "<<response.status_code();
});

但还是一样的 302。

Rest SDK 代码的问题是 http_client 初始化:

    http_client client1(U(url_));

U 宏用于与字符串文字一起使用以生成可以构造 uri 的内容。如果你在 Windows,这不应该编译,因为宏展开导致 Lurl_。显然,无论这在您的系统上产生什么结果,都会导致请求以 302 响应的内容。

有几个选项。一种是直接使用文字:

    http_client client1(U("http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"));

如果您想保留 std::string 并从中初始化客户端,您可以转换为 utility::string_t,从中可以构造 uri

    std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
    http_client client1(utility::conversions::to_string_t(url_));

一旦完成,您可能会发现您需要在 request 的延续上调用 wait 函数才能真正看到预期的输出:

     client1.request(methods::GET, builder1.to_string()).then([](http_response response)
     {
        cout<<"Response code is : "<<response.status_code();
     }).wait(); // ensure that the response gets processed

编辑:

以上内容与 Windows 相关,但与 302 响应无关。

在 Linux 上,请求始终导致 302。查看线上的请求和响应,来自 Windows 主机的请求获得 200,来自 Linux 主机的请求获得 302。原因是在 Linux 版本中,主机 header 包含一个端口号,它触发服务器以 302 响应。

Windows 请求:

GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Connection: Keep-Alive\r\n
User-Agent: cpprestsdk/2.8.0\r\n
Host: www.20min.ch\r\n
\r\n

Linux 请求:

GET /rss/rss.tmpl?type=channel&get=68 HTTP/1.1\r\n
Host: www.20min.ch:80\r\n
User-Agent:cpprestsdk/2.8.0\r\n
Connection: Keep-Alive\r\n
\r\n

您可以使用 wget 验证这是否是原因:

$ wget --header="Host: www.20min.ch" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68"

HTTP/1.1 200 行

$ wget --header="Host: www.20min.ch:80" -S "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68" --max-redirect 0

HTTP/1.1 302 找到

header 中的差异是由于不同的实现。 WinHTTP 客户端实现不会显式添加主机 header,大概是因为它依赖 WinHTTP 在内部执行此操作。不过,asio 客户端实现确实添加了它。

        // Add the Host header if user has not specified it explicitly
        if (!ctx->m_request.headers().has(header_names::host))
        {
            request_stream << "Host: " << host << ":" << port << CRLF;
        }

因此,为了获得预期的行为,可以显式设置 header 以避免添加端口信息:

std::string url_= "http://www.20min.ch/rss/rss.tmpl?type=channel&get=68";
http_client client1(utility::conversions::to_string_t(url_));
http_request request;
request.set_method(methods::GET);
request.headers().add(U("Host"), U("www.20min.ch"));
client1.request(request).then([](http_response response)
{
    std::cout<<"Response code is : "<<response.status_code();
}).wait();

通过此更改,我在 Windows 和 Linux 上都获得了 200 OK。