Керл против CPPREST

Я пытаюсь получить доступ к URL-адресу, используя CPPREST http_client :

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

Я получаю код ответа 302 для URL-перенаправления.

Но когда я пытаюсь получить доступ к тому же URL-адресу с помощью CURL, я получаю CURLE_OK.

Ниже приведены 2 части кода:

используя 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();

Вывод: успех

с помощью КППРЕСТ:

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/"));

но ответ такой же 302 с отдыхом cpp. [для перекрестной проверки пример bing

работает нормально]

ОБНОВЛЕНИЕ 2:

Метод, объясненный @Matt Weber, кажется очень полезным и законным, но я постоянно получаю код ошибки: 400 для этого, поэтому я попробовал следующие вещи: я попытался установить хост и порт для URL-адреса в uri_builder.

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.


person Hummingbird    schedule 15.03.2016    source источник


Ответы (1)


Проблема с кодом 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_));

Как только это будет сделано, вы, вероятно, обнаружите, что вам нужно вызвать функцию wait для продолжения из 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.

Запрос 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

Линукс запрос:

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

С этим изменением я получаю 200 OK как в Windows, так и в Linux.

person Matt Weber    schedule 15.03.2016
comment
Привет .. большое спасибо, моя система ожидает вывода .. это был просто минимальный код, который я написал, чтобы объяснить проблему. и я собираю Linux .. я попробую ваше решение и дам вам знать - person Hummingbird; 15.03.2016
comment
я пробовал: 'http_client client1 (utility::conversions::to_string_t(url_));' но тот же ответ 302 - person Hummingbird; 15.03.2016
comment
я также пробовал http_client client1(U(20min.ch/rss/rss. tmpl?type=channel&get=68)); но тот же ответ .. м, построенный на Linux - person Hummingbird; 15.03.2016
comment
Что произойдет, если вы инициализируете http_client значением U("http://www.20min.ch/")? Если это возвращает 200, взгляните на Bing. Запросите пример и попробуйте использовать uri_builder для остальной части URL, как это делает этот пример. - person Matt Weber; 15.03.2016
comment
Хорошо, я думаю, что понял это по-настоящему. Отредактировал мой ответ. - person Matt Weber; 15.03.2016
comment
ВАУ .. большое спасибо .. я очень ценю вашу помощь .. но с приведенным выше кодом я получаю код ошибки 400. можно было бы еще раз проверить. Я немного запутался, как и исходный URL, который у меня есть: 20min.ch/rss/rss.tmpl?type=channel&get=68, а не 20min.ch:80/rss/rss.tmpl?type=channel&get=68.. еще раз спасибо. - person Hummingbird; 15.03.2016
comment
400 предполагает, что в запросе есть что-то неправильное. Трудно сказать, не видя заголовка запроса. Код, который я включил выше, все еще работает для меня. :80 в строке url_ было ошибкой; Я включил это, когда тестировал поведение в Windows, и забыл удалить его. Вы не должны упоминать :80 в URL. - person Matt Weber; 15.03.2016
comment
Пожалуйста, просмотрите обновление, я сделал на вопрос - person Hummingbird; 16.03.2016
comment
Установка хоста URI — это не то же самое, что установка заголовка хоста HTTP-запроса, поэтому вы получаете 302 по той же причине, что и раньше. Если вы получите 400 именно с тем кодом, который я разместил, взгляните на заголовок HTTP в сетевой трассировке. Он должен выглядеть так, как показано ниже в моем ответе «Запрос Linux», за исключением заголовка хоста, который не должен включать номер порта. - person Matt Weber; 16.03.2016