Cutelyst  2.13.0
actionrest.cpp
1 /*
2  * Copyright (C) 2013-2018 Daniel Nicoletti <dantti12@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 #include "actionrest_p.h"
19 #include "context.h"
20 #include "controller.h"
21 #include "dispatcher.h"
22 
23 #include <QUrl>
24 #include <QDebug>
25 
26 using namespace Cutelyst;
27 
62 ActionREST::ActionREST(QObject *parent) : Action(new ActionRESTPrivate(this), parent)
63 {
64 }
65 
67 {
68  Q_D(const ActionREST);
69 
70  if (!Action::doExecute(c)) {
71  return false;
72  }
73 
74  return d->dispatchRestMethod(c, c->request()->method());
75 }
76 
77 ActionRESTPrivate::ActionRESTPrivate(ActionREST* q) : q_ptr(q)
78 {
79 }
80 
81 bool ActionRESTPrivate::dispatchRestMethod(Context *c, const QString &httpMethod) const
82 {
83  Q_Q(const ActionREST);
84  const QString restMethod = q->name() + QLatin1Char('_') + httpMethod;
85 
86  Controller *controller = q->controller();
87  Action *action = controller->actionFor(restMethod);
88  if (!action) {
89  // Look for non registered actions in this controller
90  const ActionList actions = controller->actions();
91  for (Action *controllerAction : actions) {
92  if (controllerAction->name() == restMethod) {
93  action = controllerAction;
94  break;
95  }
96  }
97  }
98 
99  if (action) {
100  return c->execute(action);
101  }
102 
103  bool ret = false;
104  if (httpMethod == QLatin1String("OPTIONS")) {
105  ret = returnOptions(c, q->name());
106  } else if (httpMethod == QLatin1String("HEAD")) {
107  // redispatch to GET
108  ret = dispatchRestMethod(c, QStringLiteral("GET"));
109  } else if (httpMethod != QLatin1String("not_implemented")) {
110  // try dispatching to foo_not_implemented
111  ret = dispatchRestMethod(c, QStringLiteral("not_implemented"));
112  } else {
113  // not_implemented
114  ret = returnNotImplemented(c, q->name());
115  }
116 
117  return ret;
118 }
119 
120 bool ActionRESTPrivate::returnOptions(Context *c, const QString &methodName) const
121 {
122  Response *response = c->response();
123  response->setContentType(QStringLiteral("text/plain"));
124  response->setStatus(Response::OK); // 200
125  response->setHeader(QStringLiteral("ALLOW"),
126  getAllowedMethods(c->controller(), methodName));
127  response->body().clear();
128  return true;
129 }
130 
131 bool ActionRESTPrivate::returnNotImplemented(Context *c, const QString &methodName) const
132 {
133  Response *response = c->response();
134  response->setStatus(Response::MethodNotAllowed); // 405
135  response->setHeader(QStringLiteral("ALLOW"),
136  getAllowedMethods(c->controller(), methodName));
137  const QString body = QLatin1String("Method ") + c->req()->method()
138  + QLatin1String(" not implemented for ") + c->uriFor(methodName).toString();
139  response->setBody(body);
140  return true;
141 }
142 
143 QString Cutelyst::ActionRESTPrivate::getAllowedMethods(Controller *controller, const QString &methodName) const
144 {
145  QStringList methods;
146  const QString name = methodName + QLatin1Char('_');
147  const ActionList actions = controller->actions();
148  for (Action *action : actions) {
149  const QString method = action->name();
150  if (method.startsWith(name)) {
151  methods.append(method.mid(name.size()));
152  }
153  }
154 
155  if (methods.contains(QStringLiteral("GET"))) {
156  methods.append(QStringLiteral("HEAD"));
157  }
158 
159  methods.removeAll(QStringLiteral("not_implemented"));
160  methods.sort();
161  methods.removeDuplicates();
162 
163  return methods.join(QStringLiteral(", "));
164 }
165 
166 #include "moc_actionrest.cpp"
Cutelyst::Controller
Cutelyst Controller base class
Definition: controller.h:102
Cutelyst::Controller::actions
ActionList actions() const
Definition: controller.cpp:60
Cutelyst::Response::setStatus
void setStatus(quint16 status)
Definition: response.cpp:85
Cutelyst::Context
The Cutelyst Context.
Definition: context.h:50
Cutelyst::Context::response
Response * response() const
Definition: context.cpp:110
Cutelyst::ActionREST::doExecute
bool doExecute(Context *c) override
Definition: actionrest.cpp:66
Cutelyst::Response::setContentType
void setContentType(const QString &type)
Definition: response.h:218
Cutelyst::Context::execute
bool execute(Component *code)
Definition: context.cpp:415
Cutelyst::Controller::actionFor
Action * actionFor(const QString &name) const
Definition: controller.cpp:50
Cutelyst::Response::setBody
void setBody(QIODevice *body)
Definition: response.cpp:114
Cutelyst::Response::setHeader
void setHeader(const QString &field, const QString &value)
Definition: response.cpp:306
Cutelyst::Context::uriFor
QUrl uriFor(const QString &path=QString(), const QStringList &args=QStringList(), const ParamsMultiMap &queryValues=ParamsMultiMap()) const
Definition: context.cpp:243
Cutelyst
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Cutelyst::Component::name
QString name() const
Definition: component.cpp:44
Cutelyst::ActionREST::ActionREST
ActionREST(QObject *parent=nullptr)
Definition: actionrest.cpp:62
Cutelyst::ActionREST
Automated REST Method Dispatching.
Definition: actionrest.h:28
Cutelyst::Action::doExecute
virtual bool doExecute(Context *c) override
Definition: action.cpp:147
Cutelyst::Action
This class represents a Cutelyst Action.
Definition: action.h:47
Cutelyst::ActionList
QVector< Action * > ActionList
Definition: action.h:166
Cutelyst::Response::body
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:97
Cutelyst::Response
Definition: response.h:34