cutelyst  5.0.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-2025 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "actionrest_p.h"
6 #include "context.h"
7 #include "context_p.h"
8 #include "controller.h"
9 #include "dispatcher.h"
10 
11 #include <QDebug>
12 #include <QUrl>
13 
14 using namespace Cutelyst;
15 using namespace Qt::StringLiterals;
16 
56  : Action(new ActionRESTPrivate(this), parent)
57 {
58 }
59 
61 {
62  Q_D(const ActionREST);
63 
64  const int &actionRefCount = c->d_ptr->actionRefCount; // cppcheck-suppress variableScope
65 
66  if (!Action::doExecute(c)) {
67  return false;
68  }
69 
70  Action *action = d->getRestAction(c, c->request()->method());
71  if (action) {
72  if (actionRefCount) {
73  // Async
74  c->d_ptr->pendingAsync.enqueue(action);
75  return true;
76  } else {
77  return c->execute(action);
78  }
79  }
80 
81  return true;
82 }
83 
84 ActionRESTPrivate::ActionRESTPrivate(ActionREST *q)
85  : q_ptr(q)
86 {
87 }
88 
89 Action *ActionRESTPrivate::getRestAction(Context *c, const QByteArray &httpMethod) const
90 {
91  Q_Q(const ActionREST);
92  const QString restMethod = q->name() + u'_' + QString::fromLatin1(httpMethod);
93 
94  const Controller *controller = q->controller();
95  Action *action = controller->actionFor(restMethod);
96  if (!action) {
97  // Look for non registered actions in this controller
98  const ActionList actions = controller->actions();
99  auto it = std::ranges::find_if(actions, [&restMethod](const Action *controllerAction) {
100  return controllerAction->name() == restMethod;
101  });
102  if (it != actions.end()) {
103  action = *it;
104  }
105  }
106 
107  if (!action) {
108  if (httpMethod == "HEAD") {
109  // redispatch to GET
110  action = getRestAction(c, "GET"_ba);
111  } else if (httpMethod == "OPTIONS") {
112  returnOptions(c, q->name());
113  } else if (httpMethod == "not_implemented") {
114  // not_implemented
115  returnNotImplemented(c, q->name());
116  } else {
117  // try dispatching to foo_not_implemented
118  action = getRestAction(c, "not_implemented"_ba);
119  }
120  }
121 
122  return action;
123 }
124 
125 void ActionRESTPrivate::returnOptions(Context *c, const QString &methodName) const
126 {
127  Response *response = c->response();
128  response->setContentType("text/plain"_ba);
129  response->setStatus(Response::OK); // 200
130  response->setHeader("Allow", getAllowedMethods(c->controller(), methodName));
131  response->body().clear();
132 }
133 
134 void ActionRESTPrivate::returnNotImplemented(Context *c, const QString &methodName) const
135 {
136  Response *response = c->response();
137  response->setStatus(Response::MethodNotAllowed); // 405
138  response->setHeader("Allow", getAllowedMethods(c->controller(), methodName));
139 
140  const QByteArray body = "Method " + c->req()->method() + " not implemented for " +
142  response->setBody(body);
143 }
144 
145 QByteArray Cutelyst::ActionRESTPrivate::getAllowedMethods(const Controller *controller,
146  const QString &methodName) const
147 {
148  QStringList methods;
149  const QString name = methodName + u'_';
150  const ActionList actions = controller->actions();
151  for (const Action *action : actions) {
152  const QString method = action->name();
153  if (method.startsWith(name)) {
154  methods.append(method.mid(name.size()));
155  }
156  }
157 
158  if (methods.contains(u"GET")) {
159  methods.append(u"HEAD"_s);
160  }
161 
162  methods.removeDuplicates();
163  methods.removeOne(u"not_implemented"_s);
164  methods.sort();
165 
166  return methods.join(u", ").toLatin1();
167 }
168 
169 #include "moc_actionrest.cpp"
Request request
Definition: context.h:72
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
void clear()
FullyEncoded
bool doExecute(Context *c) override
Definition: action.cpp:136
qsizetype removeDuplicates()
qsizetype size() const const
QString join(QChar separator) const const
Request req
Definition: context.h:67
QString toString(FormattingOptions options) const const
void setContentType(const QByteArray &type)
Definition: response.h:230
This class represents a Cutelyst Action.
Definition: action.h:34
A Cutelyst response.
Definition: response.h:28
The Cutelyst Context.
Definition: context.h:42
Cutelyst Controller base class.
Definition: controller.h:55
Automated REST method dispatching.
Definition: actionrest.h:14
Controller controller
Definition: context.h:76
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool doExecute(Context *c) override
Definition: actionrest.cpp:60
QString name() const noexcept
Definition: component.cpp:33
The Cutelyst namespace holds all public Cutelyst API.
bool execute(Component *code)
Definition: context.cpp:424
iterator end()
ActionREST(QObject *parent=nullptr)
Definition: actionrest.cpp:55
QString fromLatin1(QByteArrayView str)
QByteArray toLatin1() const const
QString mid(qsizetype position, qsizetype n) const const
void append(QList< T > &&value)
QByteArray & body()
Definition: response.cpp:87
void sort(Qt::CaseSensitivity cs)
Action * actionFor(QStringView name) const
Definition: controller.cpp:262
void setBody(QIODevice *body)
Definition: response.cpp:105
Response * response() const noexcept
Definition: context.cpp:98
void setStatus(quint16 status) noexcept
Definition: response.cpp:74
bool removeOne(const AT &t)
ActionList actions() const noexcept
Definition: controller.cpp:272
void setHeader(const QByteArray &key, const QByteArray &value)