diff options
author | Mikhail Svetkin <mikhail.svetkin@qt.io> | 2019-03-12 15:26:33 +0100 |
---|---|---|
committer | Mikhail Svetkin <mikhail.svetkin@qt.io> | 2019-04-04 20:50:09 +0000 |
commit | be06bd66b8c8613498df06026dd8f10164dbd4ff (patch) | |
tree | 1888ef9236986d4e07e9ed9bacae71f20e0a7c3d | |
parent | 6f7e8d28b46dea47c9179f4a69bc693e844f4d1b (diff) |
Accept a string as request method in QHttpServer::route()
Allow writing simpler source code.
For example:
httpserver.route("/", "GET|POST", [] () { return ""; })
Instead of:
httpserver.route("/", QHttpServerRequest::Method::Post | QHttpServerRequest::Method::Get, [] () { return ""; })
Change-Id: Id0a754eccaba6b5f9f3be6a3b975383eb94840a0
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
-rw-r--r-- | src/httpserver/qhttpserverrequest.h | 13 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouter.cpp | 2 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterrule.cpp | 60 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterrule.h | 5 | ||||
-rw-r--r-- | tests/auto/qhttpserver/tst_qhttpserver.cpp | 93 | ||||
-rw-r--r-- | tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp | 2 |
6 files changed, 170 insertions, 5 deletions
diff --git a/src/httpserver/qhttpserverrequest.h b/src/httpserver/qhttpserverrequest.h index 0aeb1d8..c0afbce 100644 --- a/src/httpserver/qhttpserverrequest.h +++ b/src/httpserver/qhttpserverrequest.h @@ -63,7 +63,18 @@ public: Post = 0x0008, Head = 0x0010, Options = 0x0020, - Patch = 0x0040 + Patch = 0x0040, + + All = Get | Put | Delete | Post | Head | Options | Patch, + + // Include upper-case aliases for the sake of parsing from strings: + GET = Get, + PUT = Put, + DELETE = Delete, + POST = Post, + HEAD = Head, + OPTIONS = Options, + PATCH = Patch }; Q_ENUM(Method) Q_DECLARE_FLAGS(Methods, Method) diff --git a/src/httpserver/qhttpserverrouter.cpp b/src/httpserver/qhttpserverrouter.cpp index 1dc924c..e28d5cf 100644 --- a/src/httpserver/qhttpserverrouter.cpp +++ b/src/httpserver/qhttpserverrouter.cpp @@ -281,7 +281,7 @@ bool QHttpServerRouter::addRuleImpl(QHttpServerRouterRule *rule, { Q_D(QHttpServerRouter); - if (!rule->createPathRegexp(types, d->converters)) { + if (!rule->hasValidMethods() || !rule->createPathRegexp(types, d->converters)) { delete rule; return false; } diff --git a/src/httpserver/qhttpserverrouterrule.cpp b/src/httpserver/qhttpserverrouterrule.cpp index d486218..151e411 100644 --- a/src/httpserver/qhttpserverrouterrule.cpp +++ b/src/httpserver/qhttpserverrouterrule.cpp @@ -30,7 +30,9 @@ #include <QtHttpServer/qhttpserverrouterrule.h> #include <private/qhttpserverrouterrule_p.h> +#include <private/qhttpserverrequest_p.h> +#include <QtCore/qmetaobject.h> #include <QtCore/qloggingcategory.h> #include <QtCore/qregularexpression.h> #include <QtCore/qdebug.h> @@ -39,6 +41,27 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcRouterRule, "qt.httpserver.router.rule") +static QHttpServerRequest::Methods strToMethods(const char *strMethods) +{ + QHttpServerRequest::Methods methods; + + static const auto index = QHttpServerRequest::staticMetaObject.indexOfEnumerator("Method"); + if (index == -1) { + qCWarning(lcRouterRule, "Can not find QMetaEnum for enum Method"); + return methods; + } + + static const QMetaEnum en = QHttpServerRequest::staticMetaObject.enumerator(index); + bool ok = false; + const int val = en.keysToValue(strMethods, &ok); + if (ok) + methods = static_cast<decltype(methods)>(val); + else + qCWarning(lcRouterRule, "Can not convert %s to QHttpServerRequest::Method", strMethods); + + return methods; +} + /*! \class QHttpServerRouterRule \brief The QHttpServerRouterRule is the base class for QHttpServerRouter rules. @@ -94,12 +117,14 @@ Q_LOGGING_CATEGORY(lcRouterRule, "qt.httpserver.router.rule") /*! Constructs a rule with pathPattern \a pathPattern, and routerHandler \a routerHandler. - The rule accepts any HTTP method. + The rule accepts all HTTP methods by default. + + \sq QHttpServerRequest::Method */ QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, RouterHandler &&routerHandler) : QHttpServerRouterRule(pathPattern, - QHttpServerRequest::Methods(), + QHttpServerRequest::Method::All, std::forward<RouterHandler>(routerHandler)) { } @@ -107,6 +132,10 @@ QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, /*! Constructs a rule with pathPattern \a pathPattern, methods \a methods and routerHandler \a routerHandler. + + The rule accepts any combinations of available HTTP methods. + + \sa QHttpServerRequest::Method */ QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, const QHttpServerRequest::Methods methods, @@ -119,6 +148,24 @@ QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, } /*! + Constructs a rule with pathPattern \a pathPattern, methods \a methods + and routerHandler \a routerHandler. + + \note \a methods shall be joined with | as separator (not spaces or commas) + and that either the upper-case or the capitalised form may be used. + + \sa QMetaEnum::keysToValue +*/ +QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern, + const char *methods, + RouterHandler &&routerHandler) + : QHttpServerRouterRule(pathPattern, + strToMethods(methods), + std::forward<RouterHandler>(routerHandler)) +{ +} + +/*! \internal */ QHttpServerRouterRule::QHttpServerRouterRule(QHttpServerRouterRulePrivate *d) @@ -134,6 +181,15 @@ QHttpServerRouterRule::~QHttpServerRouterRule() } /*! + Returns true if the methods is valid +*/ +bool QHttpServerRouterRule::hasValidMethods() const +{ + Q_D(const QHttpServerRouterRule); + return d->methods & QHttpServerRequest::Method::All; +} + +/*! This function is called by QHttpServerRouter when a new request is received. */ bool QHttpServerRouterRule::exec(const QHttpServerRequest &request, diff --git a/src/httpserver/qhttpserverrouterrule.h b/src/httpserver/qhttpserverrouterrule.h index 235e982..2b168b4 100644 --- a/src/httpserver/qhttpserverrouterrule.h +++ b/src/httpserver/qhttpserverrouterrule.h @@ -56,6 +56,9 @@ public: explicit QHttpServerRouterRule(const QString &pathPattern, const QHttpServerRequest::Methods methods, RouterHandler &&routerHandler); + explicit QHttpServerRouterRule(const QString &pathPattern, + const char * methods, + RouterHandler &&routerHandler); QHttpServerRouterRule(QHttpServerRouterRule &&other) = delete; QHttpServerRouterRule &operator=(QHttpServerRouterRule &&other) = delete; @@ -65,6 +68,8 @@ public: protected: bool exec(const QHttpServerRequest &request, QTcpSocket *socket) const; + bool hasValidMethods() const; + bool createPathRegexp(const std::initializer_list<int> &metaTypes, const QMap<int, QLatin1String> &converters); diff --git a/tests/auto/qhttpserver/tst_qhttpserver.cpp b/tests/auto/qhttpserver/tst_qhttpserver.cpp index 2934dc0..aeb3a92 100644 --- a/tests/auto/qhttpserver/tst_qhttpserver.cpp +++ b/tests/auto/qhttpserver/tst_qhttpserver.cpp @@ -41,6 +41,7 @@ #include <QtCore/qlist.h> #include <QtCore/qbytearray.h> #include <QtCore/qdatetime.h> +#include <QtCore/qmetaobject.h> #include <QtNetwork/qnetworkaccessmanager.h> #include <QtNetwork/qnetworkreply.h> @@ -85,6 +86,8 @@ private slots: void routeGet(); void routePost_data(); void routePost(); + void routeDelete_data(); + void routeDelete(); void invalidRouterArguments(); private: @@ -113,6 +116,24 @@ void tst_QHttpServer::initTestCase() return "Hello world post"; }); + httpserver.route("/post-and-get", "GET|POST", [] (const QHttpServerRequest &request) { + if (request.method() == QHttpServerRequest::Method::Get) + return "Hello world get"; + else if (request.method() == QHttpServerRequest::Method::Post) + return "Hello world post"; + + return "This should not work"; + }); + + httpserver.route("/any", "All", [] (const QHttpServerRequest &request) { + static const int index = QHttpServerRequest::staticMetaObject.indexOfEnumerator("Method"); + if (index == -1) + return "Error: Could not find enum Method"; + + static const QMetaEnum en = QHttpServerRequest::staticMetaObject.enumerator(index); + return en.valueToKey(static_cast<int>(request.method())); + }); + httpserver.route("/page/", [] (const qint32 number) { return QString("page: %1").arg(number); }); @@ -300,12 +321,29 @@ void tst_QHttpServer::routeGet_data() << "text/html" << "Custom router rule: 10, key=12"; + QTest::addRow("post-and-get, get") + << "/post-and-get" + << 200 + << "text/html" + << "Hello world get"; + + QTest::addRow("invalid-rule-method, get") + << "/invalid-rule-method" + << 404 + << "text/html" + << ""; + QTest::addRow("check custom type, data=1") << "/check-custom-type/1" << 200 << "text/html" << "data = 1"; + QTest::addRow("any, get") + << "/any" + << 200 + << "text/html" + << "Get"; } void tst_QHttpServer::routeGet() @@ -396,6 +434,43 @@ void tst_QHttpServer::routePost() QCOMPARE(reply->readAll(), body); } +void tst_QHttpServer::routeDelete_data() +{ + QTest::addColumn<QString>("url"); + QTest::addColumn<int>("code"); + QTest::addColumn<QString>("type"); + QTest::addColumn<QString>("data"); + + QTest::addRow("post-and-get, delete") + << "/post-and-get" + << 404 + << "text/html" + << ""; + + QTest::addRow("any, delete") + << "/any" + << 200 + << "text/html" + << "Delete"; +} + +void tst_QHttpServer::routeDelete() +{ + QFETCH(QString, url); + QFETCH(int, code); + QFETCH(QString, type); + QFETCH(QString, data); + + QNetworkAccessManager networkAccessManager; + const QUrl requestUrl(urlBase.arg(url)); + auto reply = networkAccessManager.deleteResource(QNetworkRequest(requestUrl)); + + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code); +} + struct CustomType { CustomType() {} CustomType(const QString &) {} @@ -410,6 +485,24 @@ void tst_QHttpServer::invalidRouterArguments() false); QCOMPARE( + httpserver.route("/invalid-rule-method", "GeT", [] () { + return ""; + }), + false); + + QCOMPARE( + httpserver.route("/invalid-rule-method", "Garbage", [] () { + return ""; + }), + false); + + QCOMPARE( + httpserver.route("/invalid-rule-method", "Unknown", [] () { + return ""; + }), + false); + + QCOMPARE( httpserver.route("/implicit-conversion-to-qstring-has-no-registered/", [] (const CustomType &) { return ""; diff --git a/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp b/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp index 593ffb2..61d4e19 100644 --- a/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp +++ b/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp @@ -71,7 +71,7 @@ struct HttpServer : QAbstractHttpServer { template<typename ViewHandler> void route(const char *path, ViewHandler &&viewHandler) { - route(path, QHttpServerRequest::Methods(nullptr), std::forward<ViewHandler>(viewHandler)); + route(path, QHttpServerRequest::Method::All, std::forward<ViewHandler>(viewHandler)); } bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override { |