Russian Qt Forum

Qt => Работа с сетью => Тема начата: secatorr от Сентябрь 07, 2011, 00:26



Название: QT Post-запрос
Отправлено: secatorr от Сентябрь 07, 2011, 00:26
Коллеги! Подскажите, как правильно делать POST-запрос на сервак через QNetworkAccessManager...
Вопрос уже задавался, много читал, но уже голова кругом.
Вот такой код вкратце:
Код:
QByteArray params;
    QNetworkAccessManager accessManager;
    QNetworkRequest request;
    QUrl url("http://jiffyplace.com/photo.php?a=1&b=2");
    request.setUrl(url);
    request.setRawHeader("Host", "jiffyplace.com");
    request.setRawHeader("Cache-Control", "no-cache");
    request.setRawHeader("Accept","*/*");

    params.append("a=ok");
    params.append("b=ok2");

    reply = accessManager.post(request, "a=asd");

Ну это я тут уже разные варианты добавления параметров пробовал. Как-то через раз запрос отправляется. Вообще не пойму че за хрень...
Как все происходит... Отправляю запрос без параметров - ОК. Отправляю с параметрами - нифига. Снова без параметров - нифига, больше не отправляется. Вешал слот на сигнал готовности от reply - молчит. Помогайте, братцы!


Название: Re: QT Post-запрос
Отправлено: Whiplash от Сентябрь 07, 2011, 08:48
Ну, во-первых ты АкцессМенеджер на стеке создаёшь. После выхода из метода, в котором он создан, он у тебя умирает. Хороший АкцессМенеджер - живой АкцессМенеджер. Во-вторых, я бы всё-таки установил длину тела запроса
Код:
request.setHeader(QNetworkRequest::ContentLengthHeader, parmas.length());


Название: Re: QT Post-запрос
Отправлено: Whiplash от Сентябрь 07, 2011, 08:50
Вот, кстати, рабочий код. Это реализация методов класса Request. Если надо, запощу и хидер.
Код:
Request::Request(bool syn, QObject *parent) :
    QObject(parent),
    synhro(syn)
{
    manager=new QNetworkAccessManager(this);
    QObject::connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(sent(QNetworkReply*)));
}

void Request::setParametersForRequetsSlot(const QByteArray &method, const QByteArray &addr, QList<QByteArray> &headers, const QByteArray &body)
{
    p_method=method;
    p_addr=addr;
    p_headers=headers;
    p_body=body;
}

void Request::sendRequest()
{
    sendRequest(p_method, p_addr, p_headers, p_body);
}

void Request::sendRequest(const QByteArray &method, const QByteArray &addr, QList<QByteArray> &headers, const QByteArray &body)
{
    int type=0;
    QByteArray ba=method.simplified().toLower();
    if(ba.contains("put"))type=1;
    else if(ba.contains("get"))type=2;
    //post - default

    QNetworkRequest request;
    for(int i=0;i<headers.count();i++){
        ba=headers.at(i);
        int x=ba.indexOf(": ");
        if(x<0)continue;//игнорирование неверной строчки
        if(x==ba.length()-2)continue;
        request.setRawHeader(ba.left(x),ba.right(ba.length()-x-2));
    }
    if(type!=2) request.setHeader(QNetworkRequest::ContentLengthHeader, body.length()); //не гет
    request.setUrl(QUrl(addr));

    replyBody.clear();
    replyHeaders.clear();
    ended=false;

    switch(type){
    case 0: manager->post(request,body);
        break;
    case 1: manager->put(request, body);
        break;
    case 2: manager->get(request);
    }

    //подождать
    if(synhro) while(!ended)QCoreApplication::processEvents();
}

void Request::sent(QNetworkReply *reply)
{
    replyBody=reply->readAll();
    replyHeaders=reply->rawHeaderList();
    for(int i=0;i<replyHeaders.count();i++){
        replyHeaders[i]=replyHeaders.at(i)+": "+reply->rawHeader(replyHeaders.at(i));
    }
    replyError=reply->error();
    replyErrorString=reply->errorString();
    replyURL=reply->url().toString();
    ended=true;
    emit finished();
}


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 07, 2011, 10:45
Да, думаю и хидеры пригодятся. Спасибо, буду пробовать. Моя ошибка, выходит, только в том, что в стеке АксессМенеджер создаю и не указываю длину?
А никто не работал с этими же запросами + Mobility. Мне вообще нужно с мобильного посылать? Там я так понимаю нужно сессию создавать. Просто создать и можно работать? Она как бы дает доступ к интернету для всего приложения?


Название: Re: QT Post-запрос
Отправлено: Whiplash от Сентябрь 07, 2011, 11:25
Про мобильность я ничем не помогу - кодю исключительно под десктоп.
Вот хидер
Код:
#ifndef REQUEST_H
#define REQUEST_H

#include <QObject>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkReply>

class Request : public QObject
{
    Q_OBJECT
public:
    explicit Request(bool syn=true, QObject *parent = 0);
    void sendRequest(const QByteArray &method, const QByteArray &addr, QList <QByteArray> &headers, const QByteArray &body);
    void setParametersForRequetsSlot(const QByteArray &method, const QByteArray &addr, QList <QByteArray> &headers, const QByteArray &body);
    const QByteArray& getReply(){
        return replyBody;
    }

    QList<QByteArray> getReplyHeaders(){
        return replyHeaders;
    }

    int getError(){
        return replyError;
    }

    QString getErrorString(){
        return replyErrorString;
    }

    QString getReplyURL(){
        return replyURL;
    }

    void synchronous(bool s=true){
        synhro=s;
    }

private:
    QNetworkAccessManager *manager;
    QByteArray replyBody;
    QList<QByteArray> replyHeaders;
    bool ended;
    int replyError;
    QString replyErrorString;
    QString replyURL;
    bool synhro;

    QByteArray p_method;
    QByteArray p_addr;
    QList <QByteArray> p_headers;
    QByteArray p_body;

public slots:
    void sendRequest();

private slots:
    void sent(QNetworkReply *reply);

signals:
    void finished();

};

#endif // REQUEST_H


Название: Re: QT Post-запрос
Отправлено: Whiplash от Сентябрь 07, 2011, 11:27
Щас ещё подумал... Ты в теле запроса передаёшь набор ключ-значение. А почему бы не передавать это всё в заголовке? А тело пусть пустое будет.


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 07, 2011, 12:10
Спасибо. А есть разница? А как в заголовках передавать? Просто setRawHeader(имя, значение)?


Название: Re: QT Post-запрос
Отправлено: Whiplash от Сентябрь 07, 2011, 12:20
Спасибо. А есть разница? А как в заголовках передавать? Просто setRawHeader(имя, значение)?

Ну да, это и имел в виду. Просто посоветовал попробовать, вдруг так удобнее будет.


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 07, 2011, 12:24
Спасибо за помощь. Буду пробовать.


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 08, 2011, 00:52
Кто может дать советы по работе с сетью на Symbian с Mobility, буду очень рад!


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 08, 2011, 02:26
А вот такой вопрос. Одновременно с параметрами пост-запроса хочу отправлять файл. Но! Файл отправляется, а пост-параметры нет)
Вот параметры запроса:
Код:
params.append("--AyV04a\r\n");
    params.append("content-disposition: ");
    params.append("form-data; name=\"p1\"\r\n");
    params.append("\r\n");
    params.append("Konstantin");
    params.append("\r\n");

    params.append("--AyV04a\r\n");
    params.append("content-disposition: ");
    params.append("file; name=\"file\"; filename=\""+file.fileName()+"\"\r\n");
    params.append("Content-Transfer-Encoding: binary\r\n");
    params.append("\r\n");
    params.append(file.readAll());
    params.append("\r\n");
    params.append("--AyV04a--");
    request.setHeader(QNetworkRequest::ContentLengthHeader, params.length());


Название: Re: QT Post-запрос
Отправлено: secatorr от Сентябрь 08, 2011, 02:29
Решено.
Перед
Код:
params.append("--AyV04a\r\n");
    params.append("content-disposition: ");
    params.append("form-data; name=\"p1\"\r\n");
    params.append("\r\n");
    params.appe...
было вставлено
Код:
params = "a=1&b=2";

Видать с этим проблема... Убрал и завелось! Иии-ха!


Название: Re: QT Post-запрос
Отправлено: meandnano от Сентябрь 14, 2011, 13:16
Спасибо. А есть разница? А как в заголовках передавать? Просто setRawHeader(имя, значение)?

Можешь спокойно использовать обычный QNetworkAcessManager без дополнительных танцев. Танцы пригодятся, если, например, будешь делать подключение через определенные точки доступа. А так - при запросе прога будет пытаться подключаться либо через дефолтную точку доступа, либо через открытую wlan сеть, либо предоставит выбор точки, через которую соединятся. Зависит от платформы и настроек.
У меня несколько приложений (на Symbian^3, Symbian 5th edition и Meego 1.2 Harmattan) работают через wifi/3G и все держится на стандартных настройках, post и get работают как на десктопе, при необходимости подключение к сети происходит автоматически.


Название: Re: QT Post-запрос
Отправлено: NordWest от Декабрь 15, 2011, 13:44
Цитировать
Код:
params.append("--AyV04a\r\n");
Скажите, а что значат эти строки? Это значение откуда берется. Похоже что-то типа кода сессии, его получить где-то надо?


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 01, 2012, 19:52
http://ru.wikipedia.org/wiki/HTTP#POST


Название: Re: QT Post-запрос
Отправлено: svadim от Январь 11, 2012, 18:53
Возникла схожая проблема, как и у автора темы. Решил спросить здесь же.
Имеется php-скрипт. Qt-приложение через QNetworkAccessManager отправляет ему некие данные POST-запросом. Проблема в том, что сам скрипт "отрабатывает", но параметры он не получает.
Вот сам скрипт(т.е. то что от него осталось, специально для тестов):
Код
PHP
<?php
$fileHandler = fopen("./log.txt", "w");
fwrite($fileHandler, serialize($_POST));
fclose($fileHandler);
echo "hello";
?>
 

А вот собственно и кусок Qt-кода:
Код
C++ (Qt)
QUrl url("http://127.0.0.1/test.php? ");
QByteArray body = "a=1&b=2";
 
QNetworkRequest request;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentLengthHeader, body.count());
request.setRawHeader("User-Agent", "MyApp 1.0");
request.setRawHeader("Host", url.encodedHost());
request.setRawHeader("Cache-Control", "no-cache");
request.setRawHeader("Content-Type", "text/html");
request.setRawHeader("Accept", "*/*");
 
QNetworkAccessManager *mgr = new QNetworkAccessManager(this);
QObject::connect(mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(getResult(QNetworkReply*)));
mgr->post(request, body);

Чего только не пробовал, параметры не получаю(получаю только ответ "hello"). Скрипт тестил на IIS и Apache. Машины разные. Через веб-морду скрипт нормально работает. Параметры через заголовки передавал(с пустым боди), результат тот же. Испробовал предложенный здесь класс для работы с запросами, тоже безрезультатно. Подозреваю, что всё-таки загвоздка может быть в отправляемых данных заголовка, но не хватает скилла понять где именно.
Прошу помочь тех кто в курсе данной темы.


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 11, 2012, 19:21
а не проще сделать гет запрос http://127.0.0.1/test.php?a=1&b=2

request.setHeader(QNetworkRequest::ContentLengthHeader, body.count());
request.setRawHeader("User-Agent", "MyApp 1.0");
request.setRawHeader("Host", url.encodedHost());
request.setRawHeader("Cache-Control", "no-cache");
request.setRawHeader("Content-Type", "text/html");
request.setRawHeader("Accept", "*/*");

это нахрен не надо, ты же на свой скрипт шлешь.

а твой косяк, я хз, но на первый взгляд из-за ? в конце QUrl url("http://127.0.0.1/test.php? ")


Название: Re: QT Post-запрос
Отправлено: kambala от Январь 11, 2012, 19:29
а не проще сделать гет запрос http://127.0.0.1/test.php?a=1&b=2
отличная рекомендация
а твой косяк, я хз, но на первый взгляд из-за ? в конце QUrl url("http://127.0.0.1/test.php? ")
да, скорее всего дело в двух последних символах - знаке вопроса и пробеле


Название: Re: QT Post-запрос
Отправлено: ufna от Январь 11, 2012, 19:36
ну потому что когда запрос идет таким образом, то его не нужно в "body" пихать, это сам реквест, в url'е. а то, что ты хочешь сделать, оно для реальных POST-запросов, гда данные идут в сыром виде: http://blog.ufna.ru/2010/10/16/qt-post-multipart-form-data


Название: Re: QT Post-запрос
Отправлено: svadim от Январь 11, 2012, 19:41
Убрал лишнее(собственно изначально у меня всё так и было, но не работало). Вобщем код получился такой:
Код
C++ (Qt)
QUrl url("http://127.0.0.1/test.php");
QByteArray body = "a=1&b=2";
 
QNetworkRequest request;
request.setUrl(url);
 
QNetworkAccessManager *mgr = new QNetworkAccessManager(this);
QObject::connect(mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(getResult(QNetworkReply*)));
mgr->post(request, body);
он не работает.
GET-запрос работает. Но, нужен POST. Ибо конечный front-end только через него работает.


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 11, 2012, 20:50
// QObject::connect
попробуй просто connect

инклюды покажи
#include <QNetworkReply>
этот есть?

в скрипте добавь
print_r($_POST); // или как там массив выводится, забыл уже


и покажи чо выдает


Название: Re: QT Post-запрос
Отправлено: ufna от Январь 11, 2012, 23:41
Код
C++ (Qt)
QByteArray body = "a=1&b=2";
...
mgr->post(request, body);
он не работает.
GET-запрос работает. Но, нужен POST. Ибо конечный front-end только через него работает.

А ты ссылку выше смотрел как формируется body?

У тебя два варианта:
1. реквест сделать QUrl url("http://127.0.0.1/test.php?a=1&b=2"); и проверить
2. делать как в ссылке выше, т.к. тело пост запроса - это все-таки блоки информации, а не просто "a=1&b=2"


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 12, 2012, 00:57
ufna, делать как ты советуешь надо, когда отправляешь файлы пост-запросом.

QByteArray = "a=1&b=2";
это тупо отправляется qnam.post(request, postData);

svadim или в пыхе тупит, или на локалхосте как-то по-другому.
я всегда так отправляю простой текст, у меня работает.


Название: Re: QT Post-запрос
Отправлено: ufna от Январь 12, 2012, 01:27
ну если так, то да, стоит поискать в чем трабла на локалхосте :)


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 12, 2012, 01:59
кстати, попробуй этот заголовок
request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");


Название: Re: QT Post-запрос
Отправлено: svadim от Январь 12, 2012, 10:10
// QObject::connect
попробуй просто connect

инклюды покажи
#include <QNetworkReply>
этот есть?

в скрипте добавь
print_r($_POST); // или как там массив выводится, забыл уже

и покажи чо выдает

Всё поправил, как вы говорили. Если вебом слать, то пых возврашает:
Код
PHP
Array
(
   [a] => 1
   [b] => 2
)
 
Если же слать через Qt, то получаю пустой:
Код
PHP
Array
(
)
 

кстати, попробуй этот заголовок
request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
А вот это реально помогло. Большое вам спасибо, всё заработало. Если кому-нибудь понадобится, то вот конечный вариант кода, который работает:
Код
C++ (Qt)
QNetworkRequest request(QUrl("http://127.0.0.1/test.php"));
request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); // without it - does not work
QByteArray body = "a=1&b=2";
QNetworkAccessManager *mgr = new QNetworkAccessManager(this);
connect(mgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(getResult QNetworkReply*)));
mgr->post(request, body);

Если кому интересно почему надо делать так, то здесь объяснение - http://developer.qt.nokia.com/forums/viewthread/4940 (http://developer.qt.nokia.com/forums/viewthread/4940)
Всем спасибо за помощь.


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 12, 2012, 12:52
я бы сделал так

QByteArray postData = "a=1&b=2";
QNetworkAccessManeger qnam;
QNetworkReply *reply = qnam.post(request, postData);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();

qDebug() << reply->readAll();
reply->deleteLater();


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 12, 2012, 12:56
ufna, http://developer.qt.nokia.com/doc/qt-4.8/qt4-8-intro.html

см.
QHttpMultiPart
QHttpPart


Название: Re: QT Post-запрос
Отправлено: ufna от Январь 12, 2012, 13:13
ufna, http://developer.qt.nokia.com/doc/qt-4.8/qt4-8-intro.html

см.
QHttpMultiPart
QHttpPart

Ага, видел. Но в конце 2010 еще ни намека на это не было )


Название: Re: QT Post-запрос
Отправлено: thechicho от Январь 12, 2012, 13:39
у меня тоже без них функция большая есть, влом переписывать. да и смысла не вижу)