Add support for GET methods.

Add support for HTTP GET method.
Mapping between the http GET and POST method is done by prefixing the
the keyword to the correct slot. For example, given a GET request /example,
the slot should be named get_example, so that proper routing can be
made.

Also, the GET slot receives as argument the query string as a
QVariantMap.
This commit is contained in:
Ilan Pegoraro
2016-01-07 11:20:35 +00:00
parent 9bcb80bf1b
commit f1ab4f40b9
2 changed files with 31 additions and 9 deletions

View File

@@ -20,31 +20,40 @@
* IN THE SOFTWARE.
*/
#include <iostream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QMetaMethod>
#include <QMetaObject>
#include <QMetaType>
#include <QStringList>
#include <QVariantMap>
#include "QHttpEngine/qobjecthandler.h"
#include "qobjecthandler_p.h"
#define HTTP_GET "GET"
#define HTTP_POST "POST"
#define HTTP_PUT "PUT"
#define HTTP_HEAD "HEAD"
#define HTTP_CONNECT "CONNECT"
QObjectHandlerPrivate::QObjectHandlerPrivate(QObjectHandler *handler)
: QObject(handler),
q(handler)
{
}
void QObjectHandlerPrivate::invokeSlot(QHttpSocket *socket, int index)
void QObjectHandlerPrivate::invokeSlot(QHttpSocket *socket, int index, QVariantMap *queryString)
{
// Attempt to decode the JSON from the socket
QJsonParseError error;
QJsonDocument document = QJsonDocument::fromJson(socket->readAll(), &error);
// Ensure that the document is valid
if(error.error != QJsonParseError::NoError) {
if(error.error != QJsonParseError::NoError && socket->method() != HTTP_GET) {
socket->writeError(QHttpSocket::BadRequest);
return;
}
@@ -53,7 +62,7 @@ void QObjectHandlerPrivate::invokeSlot(QHttpSocket *socket, int index)
QVariantMap retVal;
if(!q->metaObject()->method(index).invoke(q,
Q_RETURN_ARG(QVariantMap, retVal),
Q_ARG(QVariantMap, document.object().toVariantMap()))) {
Q_ARG(QVariantMap, (queryString != NULL ? *queryString : document.object().toVariantMap())))) {
socket->writeError(QHttpSocket::InternalServerError);
return;
}
@@ -86,18 +95,30 @@ QObjectHandler::QObjectHandler(QObject *parent)
void QObjectHandler::process(QHttpSocket *socket, const QString &path)
{
// Only POST requests are accepted - reject any other methods but ensure
// Only GET | POST requests are accepted - reject any other methods but ensure
// that the Allow header is set in order to comply with RFC 2616
if(socket->method() != "POST") {
socket->setHeader("Allow", "POST");
if(socket->method() != HTTP_POST && socket->method() != HTTP_GET) {
socket->setHeader("Allow", HTTP_POST);
socket->setHeader("Allow", HTTP_GET);
socket->writeError(QHttpSocket::MethodNotAllowed);
return;
}
QStringList pathSplit = path.split("?");
QVariantMap queryString;
if(socket->method() == HTTP_GET && pathSplit.length() > 1) {
QStringList qstr = pathSplit[1].split("&");
foreach(QString q, qstr) {
QStringList kv = q.split("=");
if(kv.length() != 2) break;
queryString[kv[0]] = kv[1];
}
}
QString methodName = socket->method().toLower() + "_" + pathSplit[0];
// Determine the index of the slot with the specified name - note that we
// don't need to worry about retrieving the index for deleteLater() since
// we specify the "QVariantMap" parameter type, which no parent slots use
int index = metaObject()->indexOfSlot(QString("%1(QVariantMap)").arg(path).toUtf8().data());
int index = metaObject()->indexOfSlot(QString("%1(QVariantMap)").arg(methodName).toUtf8().data());
// If the index is invalid, the "resource" was not found
if(index == -1) {
@@ -108,6 +129,7 @@ void QObjectHandler::process(QHttpSocket *socket, const QString &path)
// Ensure that the return type of the slot is QVariantMap
QMetaMethod method = metaObject()->method(index);
if(method.returnType() != QMetaType::QVariantMap) {
qCritical()<< "Return type is not valid!!!";
socket->writeError(QHttpSocket::InternalServerError);
return;
}
@@ -116,7 +138,7 @@ void QObjectHandler::process(QHttpSocket *socket, const QString &path)
// or not - if so, jump to invokeSlot(), otherwise wait for the
// readChannelFinished() signal
if(socket->bytesAvailable() >= socket->contentLength()) {
d->invokeSlot(socket, index);
d->invokeSlot(socket, index, socket->method() == HTTP_GET ? &queryString : NULL);
} else {
// Add the socket and index to the map so that the latter can be