Skip to content

Commit

Permalink
Allow use of custom data structures as router callback arguments
Browse files Browse the repository at this point in the history
Change-Id: I6dcc66a95b72bb461f237cade0352a0177065227
Reviewed-by: Qt CI Bot <[email protected]>
Reviewed-by: Edward Welbourne <[email protected]>
Reviewed-by: Jesus Fernandez <[email protected]>
Reviewed-by: Mårten Nordheim <[email protected]>
  • Loading branch information
msvetkin committed Mar 6, 2019
1 parent 572e0b1 commit 2459725
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 6 deletions.
48 changes: 44 additions & 4 deletions src/httpserver/qhttpserverrouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,44 @@ static const QMap<int, QLatin1String> defaultConverters = {
\endcode
*/

/*! \fn template <typename Type> bool QHttpServerRouter::addConverter(const QLatin1String &regexp)
Adds a new converter for type \e Type matching regular expression \a regexp.
Automatically try to register an implicit converter from QString to \e Type.
If there is already a converter of type \e Type, that converter's regexp
is replaced with \a regexp.
\code
struct CustomArg {
int data = 10;
CustomArg() {} ;
CustomArg(const QString &urlArg) : data(urlArg.toInt()) {}
};
Q_DECLARE_METATYPE(CustomArg);
QHttpServerRouter router;
router.addConverter<CustomArg>(QLatin1String("[+-]?\\d+"));
auto pageView = [] (const CustomArg &customArg) {
qDebug("data: %d", customArg.data);
};
using ViewHandler = decltype(pageView);
auto rule = new QHttpServerRouterRule(
"/<arg>/<arg>/log",
[&router, &pageView] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
// Bind and call viewHandler with match's captured string and quint32:
router.bindCaptured(pageView, match)();
});
router.addRule<ViewHandler>(rule);
\endcode
*/

/*! \fn template <typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> bool QHttpServerRouter::addRule(QHttpServerRouterRule *rule)
Adds a new \a rule.
Expand Down Expand Up @@ -134,14 +172,16 @@ static const QMap<int, QLatin1String> defaultConverters = {
\code
QHttpServerRouter router;
auto pageView = [] (const QString &page, const quint32 num) { };
auto pageView = [] (const QString &page, const quint32 num) {
qDebug("page: %s, num: %d", qPrintable(page), num);
};
using ViewHandler = decltype(pageView);
auto rule = new QHttpServerRouterRule(
"/<arg>/<arg>/log",
[&router] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
[&router, &pageView] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
// Bind and call viewHandler with match's captured string and quint32:
router.bindCaptured(pageView, match)();
});
Expand Down
13 changes: 13 additions & 0 deletions src/httpserver/qhttpserverrouter.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ class Q_HTTPSERVER_EXPORT QHttpServerRouter
QHttpServerRouter();
~QHttpServerRouter();

template<typename Type>
bool addConverter(const QLatin1String &regexp) {
static_assert(QMetaTypeId2<Type>::Defined,
"Type is not registered with Qt's meta-object system: "
"please apply Q_DECLARE_METATYPE() to it");

if (!QMetaType::registerConverter<QString, Type>())
return false;

addConverter(qMetaTypeId<Type>(), regexp);
return true;
}

void addConverter(const int type, const QLatin1String &regexp);
void removeConverter(const int);
void clearConverters();
Expand Down
13 changes: 11 additions & 2 deletions src/httpserver/qhttpserverrouterrule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,19 @@ bool QHttpServerRouterRule::createPathRegexp(const std::initializer_list<int> &m
QString pathRegexp = d->pathPattern;
const QLatin1String arg("<arg>");
for (auto type : metaTypes) {
if (type >= QMetaType::User
&& !QMetaType::hasRegisteredConverterFunction(qMetaTypeId<QString>(), type)) {
qCWarning(lcRouterRule) << QMetaType::typeName(type)
<< "has not registered a converter to QString."
<< "Use QHttpServerRouter::addConveter<Type>(converter).";
return false;
}

auto it = converters.constFind(type);
if (it == converters.end()) {
qCWarning(lcRouterRule) << "can not find converter for type:" << type;
continue;
qCWarning(lcRouterRule) << "can not find converter for type:"
<< QMetaType::typeName(type);
return false;
}

if (it->isEmpty())
Expand Down
46 changes: 46 additions & 0 deletions tests/auto/qhttpserver/tst_qhttpserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qdatetime.h>

#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qnetworkreply.h>
Expand Down Expand Up @@ -84,12 +85,20 @@ private slots:
void routeGet();
void routePost_data();
void routePost();
void invalidRouterArguments();

private:
QHttpServer httpserver;
QString urlBase;
};

struct CustomArg {
int data = 10;

CustomArg() {} ;
CustomArg(const QString &urlArg) : data(urlArg.toInt()) {}
};

void tst_QHttpServer::initTestCase()
{
httpserver.route("/test", [] (QHttpServerResponder &&responder) {
Expand Down Expand Up @@ -151,6 +160,12 @@ void tst_QHttpServer::initTestCase()
});

urlBase = QStringLiteral("http:https://localhost:%1%2").arg(httpserver.listen());


httpserver.router()->addConverter<CustomArg>(QLatin1String("[+-]?\\d+"));
httpserver.route("/check-custom-type/", [] (const CustomArg &customArg) {
return QString("data = %1").arg(customArg.data);
});
}

void tst_QHttpServer::routeGet_data()
Expand Down Expand Up @@ -278,6 +293,13 @@ void tst_QHttpServer::routeGet_data()
<< 200
<< "text/html"
<< "Custom router rule: 10, key=12";

QTest::addRow("check custom type, data=1")
<< "/check-custom-type/1"
<< 200
<< "text/html"
<< "data = 1";

}

void tst_QHttpServer::routeGet()
Expand Down Expand Up @@ -333,8 +355,32 @@ void tst_QHttpServer::routePost()
QCOMPARE(reply->readAll(), body);
}

struct CustomType {
CustomType() {}
CustomType(const QString &) {}
};

void tst_QHttpServer::invalidRouterArguments()
{
QCOMPARE(
httpserver.route("/datetime/", [] (const QDateTime &datetime) {
return QString("datetime: %1").arg(datetime.toString());
}),
false);

QCOMPARE(
httpserver.route("/implicit-conversion-to-qstring-has-no-registered/",
[] (const CustomType &) {
return "";
}),
false);
}

QT_END_NAMESPACE

Q_DECLARE_METATYPE(CustomArg);
Q_DECLARE_METATYPE(CustomType);

QTEST_MAIN(tst_QHttpServer)

#include "tst_qhttpserver.moc"

0 comments on commit 2459725

Please sign in to comment.