IT Notes

QNetworkAccessManager: Простые POST-запросы в Qt

Допустим, нам понадобилось отправлять Email-сообщения из Qt-приложения. Одним из наиболее простых способов является использование стороннего сервиса, основанного на REST API. Без умения отправки POST-запросов не обойтись. В этом нам поможет QNetworkAccessManager.

В качестве сервиса Email-рассылки выберем Mailgun. Для простейшей демонстрации нам вполне хватит его режима Sandbox. Для этого необходимо пройти базовую регистрацию и подтвердить регистрационный Email.

Чтобы полноценно пользоваться Mailgun, требуется подтверждение по смс и регистрация домена, поэтому ограничимся демо-режимом сервиса, который бесплатно позволяет отправлять Email-сообщения на наш собственный адрес (указанный при регистрации).

От Mailgun нас интересует три параметра:

  1. API-ключ вида key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
  2. URL сервиса вида https://api.mailgun.net/v3/sandboxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org/messages;
  3. Sandbox-отправитель вида Mailgun Sandbox <postmaster@sandboxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org> для заполнения пункта от.

На сайте Mailgun приводится пример вызова сервиса с помощью curl:

curl -s --user 'api:key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
    https://api.mailgun.net/v3/sandboxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org/messages \
        -F from='Mailgun Sandbox <postmaster@sandboxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org>' \
        -F to='Mikhail <xxxxxxxxxxxxxxx@gmail.com>' \
        -F subject='Hello Mikhail' \
        -F text='Congratulations Mikhail, you just sent an email with Mailgun!  You are truly awesome!'

Таким образом, нам достаточно реализовать эквивалент этого POST-запроса в коде Qt-приложения. Должно получиться что-то подобное:

qt-mail-demo

Реализация

Заголовочный файл mainwidget.h:

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QWidget>
#include <QSettings>

class QNetworkReply;
class QNetworkAccessManager;

namespace Ui {
class MainWidget;
}

class MainWidget : public QWidget {
    Q_OBJECT

public:
    explicit MainWidget( QWidget* parent = 0 );
    ~MainWidget();

private slots:
    void onSend();
    void onResponse( QNetworkReply* reply );

private:
    Ui::MainWidget* ui;
    QNetworkAccessManager* m_manager;
    QSettings m_settings;
};

#endif // MAINWIDGET_H

Здесь мы заготовили два слота: onSend() для обработки нажатия на кнопку отправки; и onResponse() для принятия ответа сервера (ответ в текстовом виде мы просто выведем в диалоговом окне). Также мы объявили поля m_manager и m_settings. Первое поле будет отвечать за всю работу по сетевому взаимодействию, а второе поможет сохранять ключевые параметры сервиса, чтобы их не пришлось вводить заново при каждом запуске программы.

Содержимое файла mainwidget.cpp:

#include "mainwidget.h"
#include "ui_mainwidget.h"

#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QSettings>
#include <QMessageBox>

static const QString SETTINGS_FILE_NAME = "config.ini";

MainWidget::MainWidget( QWidget* parent ) :
    QWidget( parent ),
    ui( new Ui::MainWidget ),
    m_manager( new QNetworkAccessManager( this ) ),
    m_settings( SETTINGS_FILE_NAME, QSettings::IniFormat ) {
    ui->setupUi( this );

    ui->edURL->setText( m_settings.value( "endpoint_url", "" ).toString() );
    ui->edKey->setText( m_settings.value( "key", "" ).toString() );
    ui->edFrom->setText( m_settings.value( "from", "" ).toString() );
    ui->edTo->setText( m_settings.value( "to", "" ).toString() );

    connect( ui->bnSend, SIGNAL( clicked( bool ) ), SLOT( onSend() ) );

    connect( m_manager, SIGNAL( finished( QNetworkReply* ) ), SLOT( onResponse( QNetworkReply* ) ) );
}

MainWidget::~MainWidget() {
    delete ui;
}

void MainWidget::onSend() {
    QUrl url( ui->edURL->text() );
    QNetworkRequest request( url );
    QString auth = QString( "%1:%2" ).arg( "api" ).arg( ui->edKey->text() );
    request.setRawHeader( "Authorization", "Basic " + auth.toLatin1().toBase64() );
    request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );

    QUrl params;
    params.addQueryItem( "from", ui->edFrom->text() );
    params.addQueryItem( "to", ui->edTo->text() );
    params.addQueryItem( "subject", ui->edSubject->text() );
    params.addQueryItem( "text", ui->txtMessage->toPlainText() );

    m_settings.setValue( "endpoint_url", ui->edURL->text() );
    m_settings.setValue( "key", ui->edKey->text() );
    m_settings.setValue( "from", ui->edFrom->text() );
    m_settings.setValue( "to", ui->edTo->text() );

    m_manager->post( request, params.encodedQuery() );
}

void MainWidget::onResponse( QNetworkReply* reply ) {
    QMessageBox::information(
        this,
        trUtf8( "Ответ сервера" ),
        reply->readAll(),
        QMessageBox::Ok
    );
}

Интерес представляет следующий фрагмент:

QUrl url( ui->edURL->text() );
QNetworkRequest request( url );
QString auth = QString( "%1:%2" ).arg( "api" ).arg( ui->edKey->text() );
request.setRawHeader( "Authorization", "Basic " + auth.toLatin1().toBase64() );
request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );

QUrl params;
params.addQueryItem( "from", ui->edFrom->text() );
params.addQueryItem( "to", ui->edTo->text() );
params.addQueryItem( "subject", ui->edSubject->text() );
params.addQueryItem( "text", ui->txtMessage->toPlainText() );

m_manager->post( request, params.encodedQuery() );

Для инициализации запроса мы заполняем URL сервиса, данные авторизации (обратите внимание, что они кодируются в base64) и тип содержимого. Далее заполняются входные параметры сервиса from, to, subject и text. Окончательно, m_manager отправляет все это на сервер с помощью вызова post().

Теперь достаточно заполнить все поля и нажать кнопку Отправить. Если все сделано правильно, то вы увидите диалоговое окно с ответом сервиса (в формате JSON), в котором говорится об успехе операции. Буквально сразу же на ваш адрес электронной почты поступит новое сообщение с указанной темой и содержимым.

Исходники

 Скачать пример работы с QNetworkAccessManager в Qt

Похожие публикации

Комментарии

У вас разве не возникло проблем с https? У меня например ругается что нету каких то ssl функций. С обычным http все хорошо.

Вероятно, зависит от конкретной сборки и версии Qt. Не помню, чтобы у меня были проблемы с запуском примера в представленном виде.