4

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

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

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

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

以下是2段代码:

使用卷曲:

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();

输出是:成功

使用 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 示例

工作正常]

更新 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。

4

1 回答 1

8

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

    http_client client1(U(url_));

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

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

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

如果要保留std::string并从中初始化客户端,则可以转换为utility::string_t可以uri构造的 a。

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

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

     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 版本中,主机头包含端口号,这是什么触发服务器以 302 响应。

窗口请求:

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

标头的差异是由于不同的实现。WinHTTP 客户端实现没有显式添加 Host 标头,可能是因为它在内部依赖 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;
        }

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

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。

于 2016-03-15T05:51:14.200 回答