cutelyst  3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
actionrest.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "actionrest_p.h"
6 #include "context.h"
7 #include "controller.h"
8 #include "dispatcher.h"
9 
10 #include <QDebug>
11 #include <QUrl>
12 
13 using namespace Cutelyst;
14 
52  : Action(new ActionRESTPrivate(this), parent)
53 {
54 }
55 
57 {
58  Q_D(const ActionREST);
59 
60  if (!Action::doExecute(c)) {
61  return false;
62  }
63 
64  return d->dispatchRestMethod(c, c->request()->method());
65 }
66 
67 ActionRESTPrivate::ActionRESTPrivate(ActionREST *q)
68  : q_ptr(q)
69 {
70 }
71 
72 bool ActionRESTPrivate::dispatchRestMethod(Context *c, const QString &httpMethod) const
73 {
74  Q_Q(const ActionREST);
75  const QString restMethod = q->name() + u'_' + httpMethod;
76 
77  Controller *controller = q->controller();
78  Action *action = controller->actionFor(restMethod);
79  if (!action) {
80  // Look for non registered actions in this controller
81  const ActionList actions = controller->actions();
82  for (Action *controllerAction : actions) {
83  if (controllerAction->name() == restMethod) {
84  action = controllerAction;
85  break;
86  }
87  }
88  }
89 
90  if (action) {
91  return c->execute(action);
92  }
93 
94  bool ret = false;
95  if (httpMethod.compare(u"OPTIONS") == 0) {
96  ret = returnOptions(c, q->name());
97  } else if (httpMethod.compare(u"HEAD") == 0) {
98  // redispatch to GET
99  ret = dispatchRestMethod(c, QStringLiteral("GET"));
100  } else if (httpMethod.compare(u"not_implemented") != 0) {
101  // try dispatching to foo_not_implemented
102  ret = dispatchRestMethod(c, QStringLiteral("not_implemented"));
103  } else {
104  // not_implemented
105  ret = returnNotImplemented(c, q->name());
106  }
107 
108  return ret;
109 }
110 
111 bool ActionRESTPrivate::returnOptions(Context *c, const QString &methodName) const
112 {
113  Response *response = c->response();
114  response->setContentType(QStringLiteral("text/plain"));
115  response->setStatus(Response::OK); // 200
116  response->setHeader(QStringLiteral("ALLOW"), getAllowedMethods(c->controller(), methodName));
117  response->body().clear();
118  return true;
119 }
120 
121 bool ActionRESTPrivate::returnNotImplemented(Context *c, const QString &methodName) const
122 {
123  Response *response = c->response();
124  response->setStatus(Response::MethodNotAllowed); // 405
125  response->setHeader(QStringLiteral("ALLOW"), getAllowedMethods(c->controller(), methodName));
126  const QString body = QLatin1String("Method ") + c->req()->method() +
127  QLatin1String(" not implemented for ") + c->uriFor(methodName).toString();
128  response->setBody(body);
129  return true;
130 }
131 
132 QString Cutelyst::ActionRESTPrivate::getAllowedMethods(Controller *controller,
133  const QString &methodName) const
134 {
135  QStringList methods;
136  const QString name = methodName + u'_';
137  const ActionList actions = controller->actions();
138  for (Action *action : actions) {
139  const QString method = action->name();
140  if (method.startsWith(name)) {
141  methods.append(method.mid(name.size()));
142  }
143  }
144 
145  if (methods.contains(u"GET")) {
146  methods.append(QStringLiteral("HEAD"));
147  }
148 
149  methods.removeAll(QStringLiteral("not_implemented"));
150  methods.sort();
151  methods.removeDuplicates();
152 
153  return methods.join(u", ");
154 }
155 
156 #include "moc_actionrest.cpp"
void clear()
void setContentType(const QString &type)
Definition: response.h:220
virtual bool doExecute(Context *c) override
Definition: action.cpp:136
void setHeader(const QString &field, const QString &value)
int removeDuplicates()
int size() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
Action * actionFor(const QString &name) const
Definition: controller.cpp:36
QString join(const QString &separator) const const
QUrl uriFor(const QString &path=QString(), const QStringList &args=QStringList(), const ParamsMultiMap &queryValues=ParamsMultiMap()) const
Definition: context.cpp:235
QString toString(QUrl::FormattingOptions options) const const
This class represents a Cutelyst Action.
Definition: action.h:34
The Cutelyst Context.
Definition: context.h:38
Cutelyst Controller base class
Definition: controller.h:87
void append(const T &value)
Automated REST Method Dispatching.
Definition: actionrest.h:15
QString name() const
Definition: component.cpp:33
bool doExecute(Context *c) override
Definition: actionrest.cpp:56
int removeAll(const T &value)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
bool execute(Component *code)
Definition: context.cpp:429
ActionREST(QObject *parent=nullptr)
Definition: actionrest.cpp:51
QString mid(int position, int n) const const
Q_REQUIRED_RESULT QByteArray & body()
Definition: response.cpp:83
void sort(Qt::CaseSensitivity cs)
void setBody(QIODevice *body)
Definition: response.cpp:100
Response * response() const noexcept
Definition: context.cpp:96
ActionList actions() const
Definition: controller.cpp:56
void setStatus(quint16 status) noexcept
Definition: response.cpp:70