From e0183207e6a4a71bc85aaa521920670fc9e425e5 Mon Sep 17 00:00:00 2001 From: Lennart Sauerbeck Date: Fri, 7 Jul 2017 14:59:11 +0200 Subject: [PATCH 1/2] QJsonDocument: Avoid QJsonObject and QJsonArray in interface Previously QJsonObject was used to pass response data. That made it impossible to pass an array as data to the client. To allow both single objects and whole lists of objects, QJsonDocument is now used, which is capable of containing both an object and an array. Signed-off-by: Lennart Sauerbeck --- src/httpdata.cpp | 6 +++--- src/httpdata.h | 2 +- src/httprequest.cpp | 6 ++++-- src/httprequest.h | 4 ++-- src/httpresponse.cpp | 13 ++++++------- src/httpresponse.h | 10 +++++----- src/httpserver.cpp | 26 +++++++++++++------------- src/swagger.cpp | 2 +- src/utils.h | 27 ++++++--------------------- 9 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/httpdata.cpp b/src/httpdata.cpp index 5d07626a..5b4b02ef 100644 --- a/src/httpdata.cpp +++ b/src/httpdata.cpp @@ -38,7 +38,7 @@ HttpResponse& HttpData::getResponse() return m_HttpResponse; } -void HttpData::setResponse(const QJsonObject& json) +void HttpData::setResponse(const QJsonDocument& json) { getResponse().setJson(json); } @@ -51,7 +51,7 @@ void HttpData::setErrorResponse(const QString& msg) void HttpData::setErrorResponse(const QString &msg, HttpError code) { getResponse().setStatus(static_cast(code)); - getResponse().getJson()["error"] = msg; + getResponse().getJson().setObject({{"error", msg}}); } void HttpData::setErrorResponse(const QJsonObject& json) @@ -62,7 +62,7 @@ void HttpData::setErrorResponse(const QJsonObject& json) void HttpData::setErrorResponse(const QJsonObject &json, HttpError code) { getResponse().setStatus(static_cast(code)); - getResponse().setJson(json); + getResponse().setJson(QJsonDocument(json)); } const QUuid& HttpData::getUid() const diff --git a/src/httpdata.h b/src/httpdata.h index d1110d97..d397e093 100644 --- a/src/httpdata.h +++ b/src/httpdata.h @@ -46,7 +46,7 @@ class QTTPSHARED_EXPORT HttpData * * This is the same as getResponse().setJson(); */ - void setResponse(const QJsonObject& json); + void setResponse(const QJsonDocument &json); //! Quick and easy way to set error messages. void setErrorResponse(const QString& msg); diff --git a/src/httprequest.cpp b/src/httprequest.cpp index 0d895529..05e414ba 100644 --- a/src/httprequest.cpp +++ b/src/httprequest.cpp @@ -59,7 +59,7 @@ HttpMethod HttpRequest::getMethod(bool strictComparison) const return m_MethodEnum; } -const QJsonObject& HttpRequest::getJson() const +const QJsonDocument& HttpRequest::getJson() const { if(!m_Json.isEmpty()) { @@ -86,7 +86,9 @@ const QJsonObject& HttpRequest::getJson() const QList > list = getQuery().queryItems(); for(auto i = list.begin(); i != list.end(); ++i) { - m_Json.insert(i->first, i->second); + QJsonObject old = m_Json.object(); + old.insert(i->first, i->second); + m_Json.setObject(old); } return m_Json; diff --git a/src/httprequest.h b/src/httprequest.h index ffca1142..8ec965e8 100644 --- a/src/httprequest.h +++ b/src/httprequest.h @@ -54,7 +54,7 @@ class QTTPSHARED_EXPORT HttpRequest * should be called before dispatching to multiple threads (if there's some * reason for multiple threads). */ - const QJsonObject& getJson() const; + const QJsonDocument& getJson() const; const QByteArray& getBody() const; @@ -84,7 +84,7 @@ class QTTPSHARED_EXPORT HttpRequest native::http::QttpRequest * m_Request; HttpUrl m_HttpUrl; mutable HttpMethod m_MethodEnum; - mutable QJsonObject m_Json; + mutable QJsonDocument m_Json; QUrlQuery m_Query; }; diff --git a/src/httpresponse.cpp b/src/httpresponse.cpp index 81e65a0c..ecf762f2 100644 --- a/src/httpresponse.cpp +++ b/src/httpresponse.cpp @@ -46,17 +46,17 @@ HttpStatus HttpResponse::getStatus() const return m_Status; } -QJsonObject& HttpResponse::getJson() +QJsonDocument& HttpResponse::getJson() { return m_Json; } -const QJsonObject& HttpResponse::getJson() const +const QJsonDocument& HttpResponse::getJson() const { return m_Json; } -void HttpResponse::setJson(const QJsonObject& json) +void HttpResponse::setJson(const QJsonDocument& json) { m_Json = json; } @@ -80,16 +80,15 @@ bool HttpResponse::finish(const string& body) return m_Response->close(); } -bool HttpResponse::finish(const QJsonObject& json) +bool HttpResponse::finish(const QJsonDocument& json) { LOG_TRACE; setHeader("Content-Type", "application/json"); - QJsonDocument doc(json); #ifdef QTTP_FORMAT_JSON_RESPONSE - return finish(doc.toJson(QJsonDocument::Indented)); + return finish(json.toJson(QJsonDocument::Indented)); #else - return finish(doc.toJson(QJsonDocument::Compact)); + return finish(json.toJson(QJsonDocument::Compact)); #endif } diff --git a/src/httpresponse.h b/src/httpresponse.h index d37e4c3a..e32dc586 100644 --- a/src/httpresponse.h +++ b/src/httpresponse.h @@ -44,9 +44,9 @@ class QTTPSHARED_EXPORT HttpResponse * As an alternative, the caller may optionally complete the transaction * with finishResponse(). */ - QJsonObject& getJson(); - const QJsonObject& getJson() const; - void setJson(const QJsonObject& json); + QJsonDocument &getJson(); + const QJsonDocument& getJson() const; + void setJson(const QJsonDocument &json); /** * @brief Preferred method when working with the json object. Populate @@ -63,7 +63,7 @@ class QTTPSHARED_EXPORT HttpResponse */ bool finish(const std::string& body); bool finish(const QByteArray& bytes); - bool finish(const QJsonObject& json); + bool finish(const QJsonDocument &json); /** * @return Boolean indicating if finishResponse() has been called. @@ -113,7 +113,7 @@ class QTTPSHARED_EXPORT HttpResponse native::http::QttpResponse * m_Response; HttpStatus m_Status; - QJsonObject m_Json; + QJsonDocument m_Json; quint32 m_ControlFlag; }; diff --git a/src/httpserver.cpp b/src/httpserver.cpp index a7a9e139..206bf718 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -587,8 +587,8 @@ function HttpServer::defaultEventCallback() const default: STATS_INC("http:method:unknown"); response.setStatus(HttpStatus::BAD_REQUEST); - QJsonObject& json = data.getResponse().getJson(); - json["error"] = QSTR("Invalid HTTP method"); + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"error", QSTR("Invalid HTTP method")}}); return; } @@ -597,8 +597,8 @@ function HttpServer::defaultEventCallback() const { LOG_ERROR("Invalid route"); response.setStatus(HttpStatus::INTERNAL_SERVER_ERROR); - QJsonObject& json = data.getResponse().getJson(); - json["error"] = QSTR("Internal error"); + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"error", QSTR("Internal error")}}); return; } @@ -682,8 +682,8 @@ function HttpServer::defaultEventCallback() const if(!searchAndServeFile(data)) { response.setStatus(HttpStatus::BAD_REQUEST); - QJsonObject& json = data.getResponse().getJson(); - json["error"] = QSTR("Invalid request"); + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"error", QSTR("Invalid request")}}); performPostprocessing(data); } } @@ -694,20 +694,20 @@ function HttpServer::defaultEventCallback() const { LOG_ERROR("Exception caught" << e.what()); response.setStatus(HttpStatus::INTERNAL_SERVER_ERROR); - QJsonObject& json = data.getResponse().getJson(); - json["error"] = e.what(); + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"error", e.what()}}); } catch(const QJsonObject& e) { LOG_ERROR("JSON caught" << e); response.setStatus(HttpStatus::INTERNAL_SERVER_ERROR); - data.getResponse().getJson() = e; + data.getResponse().getJson().setObject(e); } catch(...) { response.setStatus(HttpStatus::INTERNAL_SERVER_ERROR); - QJsonObject& json = data.getResponse().getJson(); - json["error"] = QSTR("Internal server error"); + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"error", QSTR("Internal server error")}}); } if(!response.isFinished()) @@ -734,8 +734,8 @@ function HttpServer::defaultEventCallback() const obj["timeElapsedMs"] = (qreal)(uv_hrtime() - request.getTimestamp()) / (qreal)1000000.00; - QJsonObject& json = data.getResponse().getJson(); - json["requestMetadata"] = obj; + QJsonDocument& json = data.getResponse().getJson(); + json.setObject({{"requestMetadata", obj}}); } if( !response.finish()) diff --git a/src/swagger.cpp b/src/swagger.cpp index f409577f..92f8a5c2 100644 --- a/src/swagger.cpp +++ b/src/swagger.cpp @@ -235,7 +235,7 @@ void Swagger::onGet(HttpData& data) { if(m_IsEnabled) { - data.getResponse().setJson(m_Response); + data.getResponse().setJson(QJsonDocument(m_Response)); } else { diff --git a/src/utils.h b/src/utils.h index 138f58d2..bea87575 100644 --- a/src/utils.h +++ b/src/utils.h @@ -292,34 +292,19 @@ class QTTPSHARED_EXPORT Utils return Utils::toByteArray(buffer.str()); } - static inline QJsonObject toJson(const std::stringstream& buffer, QJsonParseError* error = 0) + static inline QJsonDocument toJson(const std::stringstream& buffer, QJsonParseError* error = 0) { - return QJsonDocument::fromJson(toByteArray(buffer), error).object(); + return QJsonDocument::fromJson(toByteArray(buffer), error); } - static inline QJsonObject toJson(const std::string& str, QJsonParseError* error = 0) + static inline QJsonDocument toJson(const std::string& str, QJsonParseError* error = 0) { - return QJsonDocument::fromJson(toByteArray(str), error).object(); + return QJsonDocument::fromJson(toByteArray(str), error); } - static inline QJsonObject toJson(QByteArray bytes, QJsonParseError* error = 0) + static inline QJsonDocument toJson(QByteArray bytes, QJsonParseError* error = 0) { - return QJsonDocument::fromJson(bytes, error).object(); - } - - static inline QJsonArray toArray(const std::stringstream& buffer, QJsonParseError* error = 0) - { - return QJsonDocument::fromJson(toByteArray(buffer), error).array(); - } - - static inline QJsonArray toArray(const std::string& str, QJsonParseError* error = 0) - { - return QJsonDocument::fromJson(toByteArray(str), error).array(); - } - - static inline QJsonArray toArray(QByteArray bytes, QJsonParseError* error = 0) - { - return QJsonDocument::fromJson(bytes, error).array(); + return QJsonDocument::fromJson(bytes, error); } }; From 41a896ab9fad6992a9661d8074e90aeb452eb90d Mon Sep 17 00:00:00 2001 From: Lennart Sauerbeck Date: Thu, 31 Aug 2017 10:54:16 +0200 Subject: [PATCH 2/2] Fixed tests Signed-off-by: Lennart Sauerbeck --- test/qttptest/qttptest.cpp | 22 ++++++++++++++-------- test/qttptest/qttptest.h | 26 ++++++++++++++++++-------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/test/qttptest/qttptest.cpp b/test/qttptest/qttptest.cpp index 34db4c71..ab3f3b9c 100644 --- a/test/qttptest/qttptest.cpp +++ b/test/qttptest/qttptest.cpp @@ -198,16 +198,18 @@ void QttpTest::initTestCase() QVERIFY(httpSvr->initialize() == true); auto action = httpSvr->createAction("", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "C++ FTW"; + data.getResponse().getJson().setObject(json); }); httpSvr->addProcessor(); action = httpSvr->createAction("echo", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); auto& query = data.getRequest().getQuery(); json["response"] = "C++ FTW " + query.queryItemValue("id"); + data.getResponse().getJson().setObject(json); }); auto result = httpSvr->registerRoute("get", "", "/echo/:id"); @@ -217,8 +219,9 @@ void QttpTest::initTestCase() QVERIFY(result == true); action = httpSvr->createAction("echobody", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); - json["response"] = data.getRequest().getJson(); + QJsonObject json = data.getResponse().getJson().object(); + json["response"] = data.getRequest().getJson().object(); + data.getResponse().getJson().setObject(json); }); result = httpSvr->registerRoute("post", "echobody", "/echobody"); @@ -257,8 +260,9 @@ void QttpTest::initTestCase() // Uses a raw std::function based callback. action = httpSvr->createAction("test", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Test C++ FTW"; + data.getResponse().getJson().setObject(json); // NOTE: This terminates early so we should not expect any post-processing. data.getResponse().finish(); @@ -272,8 +276,9 @@ void QttpTest::initTestCase() QVERIFY(result == true); action = httpSvr->createAction("terminates", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Test C++ FTW"; + data.getResponse().getJson().setObject(json); // NOTE: This terminates early so we should not expect any post-processing. data.getResponse().terminate(); }); @@ -283,9 +288,10 @@ void QttpTest::initTestCase() QVERIFY(result == true); action = httpSvr->createAction("regex", [](HttpData& data) { - QJsonObject& json = data.getResponse().getJson(); - QString name = data.getRequest().getJson()["name"].toString(); + QJsonObject json = data.getResponse().getJson().object(); + QString name = data.getRequest().getJson().object()["name"].toString(); json["response"] = name; + data.getResponse().getJson().setObject(json); }); result = httpSvr->registerRoute(qttp::GET, "regex", "/regex/:name([A-Za-z]+)"); diff --git a/test/qttptest/qttptest.h b/test/qttptest/qttptest.h index 0dc5f4fa..75e0a6df 100644 --- a/test/qttptest/qttptest.h +++ b/test/qttptest/qttptest.h @@ -20,8 +20,9 @@ class SampleAction : public Action void onAction(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW"; + data.getResponse().getJson().setObject(json); } const char* getName() const @@ -36,29 +37,33 @@ class SampleActionWithHttpMethods : public Action void onGet(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW Get"; + data.getResponse().getJson().setObject(json); } void onPost(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW Post"; + data.getResponse().getJson().setObject(json); } void onPut(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW Put"; + data.getResponse().getJson().setObject(json); } void onDelete(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW Delete"; + data.getResponse().getJson().setObject(json); } const char* getName() const @@ -77,8 +82,9 @@ class ActionWithParameter : public Action void onAction(HttpData& data) { TEST_TRACE; - QJsonObject& json = data.getResponse().getJson(); + QJsonObject json = data.getResponse().getJson().object(); json["response"] = "Sample C++ FTW With Parameter " + m_Param; + data.getResponse().getJson().setObject(json); } const char* getName() const @@ -100,13 +106,17 @@ class SampleProcessor : public Processor void preprocess(HttpData& data) { TEST_TRACE; - data.getResponse().getJson()["preprocess"] = true; + QJsonObject obj = data.getResponse().getJson().object(); + obj["preprocess"] = true; + data.getResponse().getJson().setObject(obj); } void postprocess(HttpData& data) { TEST_TRACE; - data.getResponse().getJson()["postprocess"] = true; + QJsonObject obj = data.getResponse().getJson().object(); + obj["postprocess"] = true; + data.getResponse().getJson().setObject(obj); } };