QRestAM custom HTTP 'method' support

This commit adds support for sending custom, non-standard, HTTP
methods / verbs.

Fixes: QTBUG-116262
Change-Id: I77addb389a7e4346b63526176bf8323696a7a337
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Juha Vuolle 2023-10-31 15:44:48 +02:00
parent 98b240d00a
commit a80ed49b10
6 changed files with 129 additions and 1 deletions

View File

@ -109,6 +109,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li \c put() \li \c put()
\li \c head() \li \c head()
\li \c deleteResource() \li \c deleteResource()
\li \c sendCustomRequest()
\row \row
\li No data \li No data
\li X \li X
@ -116,6 +117,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li - \li -
\li X \li X
\li X \li X
\li -
\row \row
\li QByteArray \li QByteArray
\li X \li X
@ -123,6 +125,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li X
\row \row
\li QJsonObject *) \li QJsonObject *)
\li X \li X
@ -130,6 +133,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li -
\row \row
\li QJsonArray *) \li QJsonArray *)
\li - \li -
@ -137,6 +141,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li -
\row \row
\li QVariantMap **) \li QVariantMap **)
\li - \li -
@ -144,6 +149,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li -
\row \row
\li QHttpMultiPart \li QHttpMultiPart
\li - \li -
@ -151,6 +157,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li X
\row \row
\li QIODevice \li QIODevice
\li X \li X
@ -158,6 +165,7 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\li X \li X
\li - \li -
\li - \li -
\li X
\endtable \endtable
*) QJsonObject and QJsonArray are sent in \l QJsonDocument::Compact format, *) QJsonObject and QJsonArray are sent in \l QJsonDocument::Compact format,
@ -476,6 +484,44 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
QRestAccessManager::requestFinished() QRestAccessManager::requestFinished()
*/ */
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::sendCustomRequest(
const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
Issues \a request based HTTP request with custom \a method and the
provided \a data.
The optional \a callback and \a context object can be provided for
handling the request completion as illustrated below:
\snippet code/src_network_access_qrestaccessmanager.cpp 9
Alternatively the signals of the returned QRestReply* object can be
used. For further information see
\l {Issuing Network Requests and Handling Replies}.
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::sendCustomRequest(
const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/*!
\fn template<typename Functor, if_compatible_callback<Functor>> QRestReply *QRestAccessManager::sendCustomRequest(
const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
const ContextTypeForFunctor<Functor> *context,
Functor &&callback)
\overload
*/
/* /*
Memory management/object ownership: Memory management/object ownership:
- QRestAM is parent of QNAM and QRestReplies - QRestAM is parent of QNAM and QRestReplies
@ -754,6 +800,36 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, const QByteArray &data,
const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->sendCustomRequest(request, method, data); },
context, slot);
}
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, QIODevice *data,
const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->sendCustomRequest(request, method, data); },
context, slot);
}
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, QHttpMultiPart *data,
const QObject *context,
QtPrivate::QSlotObjectBase *slot)
{
Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->sendCustomRequest(request, method, data); },
context, slot);
}
QRestReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *networkReply, QRestReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *networkReply,
const QObject *contextObject, const QObject *contextObject,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)

View File

@ -32,7 +32,6 @@ QRestReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data,
const QObject *context, QtPrivate::QSlotObjectBase *slot); \ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
/* end */ /* end */
#define QREST_METHOD_NO_DATA(METHOD) \ #define QREST_METHOD_NO_DATA(METHOD) \
public: \ public: \
template <typename Functor, if_compatible_callback<Functor> = true> \ template <typename Functor, if_compatible_callback<Functor> = true> \
@ -52,6 +51,26 @@ QRestReply *METHOD##NoDataImpl(const QNetworkRequest &request,
const QObject *context, QtPrivate::QSlotObjectBase *slot); \ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
/* end */ /* end */
#define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \
public: \
template <typename Functor, if_compatible_callback<Functor> = true> \
QRestReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \
const ContextTypeForFunctor<Functor> *context, \
Functor &&callback) \
{ \
return customWithDataImpl(request, method, data, context, \
QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
} \
QRestReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \
{ \
return customWithDataImpl(request, method, data, nullptr, nullptr); \
} \
private: \
QRestReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \
DATA data, const QObject* context, \
QtPrivate::QSlotObjectBase *slot); \
/* end */
class QRestAccessManagerPrivate; class QRestAccessManagerPrivate;
class Q_NETWORK_EXPORT QRestAccessManager : public QObject class Q_NETWORK_EXPORT QRestAccessManager : public QObject
{ {
@ -95,6 +114,9 @@ public:
QREST_METHOD_WITH_DATA(put, const QByteArray &) QREST_METHOD_WITH_DATA(put, const QByteArray &)
QREST_METHOD_WITH_DATA(put, QHttpMultiPart *) QREST_METHOD_WITH_DATA(put, QHttpMultiPart *)
QREST_METHOD_WITH_DATA(put, QIODevice *) QREST_METHOD_WITH_DATA(put, QIODevice *)
QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &)
QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)
Q_SIGNALS: Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY #ifndef QT_NO_NETWORKPROXY
@ -113,6 +135,7 @@ private:
#undef QREST_METHOD_NO_DATA #undef QREST_METHOD_NO_DATA
#undef QREST_METHOD_WITH_DATA #undef QREST_METHOD_WITH_DATA
#undef QREST_METHOD_CUSTOM_WITH_DATA
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -82,3 +82,10 @@ manager->deleteResource(request, this, [this](QRestReply *reply) {
}); });
//! [8] //! [8]
//! [9]
manager->sendCustomRequest(request, "MYMETHOD", myData, this, [this](QRestReply *reply) {
if (reply->isSuccess())
// ...
});
//! [9]

View File

@ -151,6 +151,8 @@ bool HttpTestServer::readMethod(QTcpSocket *socket)
method = Method::Post; method = Method::Post;
else if (fragment == "DELETE") else if (fragment == "DELETE")
method = Method::Delete; method = Method::Delete;
else if (fragment == "FOOBAR") // used by custom verb/method tests
method = Method::Custom;
else else
qWarning("Invalid operation %s", fragment.data()); qWarning("Invalid operation %s", fragment.data());

View File

@ -56,6 +56,7 @@ public:
Put, Put,
Post, Post,
Delete, Delete,
Custom,
} method = Method::Unknown; } method = Method::Unknown;
// Parsing helpers for incoming data => HttpData // Parsing helpers for incoming data => HttpData

View File

@ -108,6 +108,7 @@ void tst_QRestAccessManager::networkRequestReply()
const QByteArray methodPOST{"POST"_ba}; const QByteArray methodPOST{"POST"_ba};
const QByteArray methodGET{"GET"_ba}; const QByteArray methodGET{"GET"_ba};
const QByteArray methodPUT{"PUT"_ba}; const QByteArray methodPUT{"PUT"_ba};
const QByteArray methodCUSTOM{"FOOBAR"_ba};
// DELETE // DELETE
manager.deleteResource(request, this, callback); manager.deleteResource(request, this, callback);
@ -136,6 +137,22 @@ void tst_QRestAccessManager::networkRequestReply()
VERIFY_REPLY_OK(methodGET); VERIFY_REPLY_OK(methodGET);
QCOMPARE(serverSideRequest.body, ioDeviceData); QCOMPARE(serverSideRequest.body, ioDeviceData);
// CUSTOM
manager.sendCustomRequest(request, methodCUSTOM, byteArrayData, this, callback);
VERIFY_REPLY_OK(methodCUSTOM);
QCOMPARE(serverSideRequest.body, byteArrayData);
manager.sendCustomRequest(request, methodCUSTOM, &bufferIoDevice, this, callback);
VERIFY_REPLY_OK(methodCUSTOM);
QCOMPARE(serverSideRequest.body, ioDeviceData);
multiPart.reset(new QHttpMultiPart(QHttpMultiPart::FormDataType));
multiPart->append(part);
manager.sendCustomRequest(request, methodCUSTOM, multiPart.get(), this, callback);
VERIFY_REPLY_OK(methodCUSTOM);
QVERIFY(serverSideRequest.body.contains("--boundary"_ba));
QVERIFY(serverSideRequest.body.contains("multipart_text"_ba));
// POST // POST
manager.post(request, byteArrayData, this, callback); manager.post(request, byteArrayData, this, callback);
VERIFY_REPLY_OK(methodPOST); VERIFY_REPLY_OK(methodPOST);
@ -206,6 +223,8 @@ void tst_QRestAccessManager::networkRequestReply()
//manager.head(request, "f"_ba); // data not allowed //manager.head(request, "f"_ba); // data not allowed
//manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature //manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature
//manager.get(request, this, [](int param){}); // Wrong callback signature //manager.get(request, this, [](int param){}); // Wrong callback signature
//manager.sendCustomRequest(request, this, [](){}); // No verb && no data
//manager.sendCustomRequest(request, "FOOBAR", this, [](){}); // No verb || no data
} }
void tst_QRestAccessManager::abort() void tst_QRestAccessManager::abort()