cutelyst  4.3.0
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 
54  : Action(new ActionRESTPrivate(this), parent)
55 {
56 }
57 
59 {
60  Q_D(const ActionREST);
61 
62  if (!Action::doExecute(c)) {
63  return false;
64  }
65 
66  return d->dispatchRestMethod(c, c->request()->method());
67 }
68 
69 ActionRESTPrivate::ActionRESTPrivate(ActionREST *q)
70  : q_ptr(q)
71 {
72 }
73 
74 bool ActionRESTPrivate::dispatchRestMethod(Context *c, const QByteArray &httpMethod) const
75 {
76  Q_Q(const ActionREST);
77  const QString restMethod = q->name() + u'_' + QString::fromLatin1(httpMethod);
78 
79  Controller *controller = q->controller();
80  Action *action = controller->actionFor(restMethod);
81  if (!action) {
82  // Look for non registered actions in this controller
83  const ActionList actions = controller->actions();
84  for (Action *controllerAction : actions) {
85  if (controllerAction->name() == restMethod) {
86  action = controllerAction;
87  break;
88  }
89  }
90  }
91 
92  if (action) {
93  return c->execute(action);
94  }
95 
96  bool ret = false;
97  if (httpMethod.compare("OPTIONS") == 0) {
98  ret = returnOptions(c, q->name());
99  } else if (httpMethod.compare("HEAD") == 0) {
100  // redispatch to GET
101  ret = dispatchRestMethod(c, "GET"_qba);
102  } else if (httpMethod.compare("not_implemented") != 0) {
103  // try dispatching to foo_not_implemented
104  ret = dispatchRestMethod(c, "not_implemented"_qba);
105  } else {
106  // not_implemented
107  ret = returnNotImplemented(c, q->name());
108  }
109 
110  return ret;
111 }
112 
113 bool ActionRESTPrivate::returnOptions(Context *c, const QString &methodName) const
114 {
115  Response *response = c->response();
116  response->setContentType("text/plain"_qba);
117  response->setStatus(Response::OK); // 200
118  response->setHeader("Allow"_qba, getAllowedMethods(c->controller(), methodName));
119  response->body().clear();
120  return true;
121 }
122 
123 bool ActionRESTPrivate::returnNotImplemented(Context *c, const QString &methodName) const
124 {
125  Response *response = c->response();
126  response->setStatus(Response::MethodNotAllowed); // 405
127  response->setHeader("Allow"_qba, getAllowedMethods(c->controller(), methodName));
128  const QByteArray body = "Method " + c->req()->method() + " not implemented for " +
129  c->uriFor(methodName).toString().toLatin1();
130  response->setBody(body);
131  return true;
132 }
133 
134 QByteArray Cutelyst::ActionRESTPrivate::getAllowedMethods(Controller *controller,
135  const QString &methodName) const
136 {
137  QStringList methods;
138  const QString name = methodName + u'_';
139  const ActionList actions = controller->actions();
140  for (Action *action : actions) {
141  const QString method = action->name();
142  if (method.startsWith(name)) {
143  methods.append(method.mid(name.size()));
144  }
145  }
146 
147  if (methods.contains(u"GET")) {
148  methods.append(QStringLiteral("HEAD"));
149  }
150 
151  methods.removeAll(QStringLiteral("not_implemented"));
152  methods.sort();
153  methods.removeDuplicates();
154 
155  return methods.join(u", ").toLatin1();
156 }
157 
158 #include "moc_actionrest.cpp"
Request request
Definition: context.h:72
bool contains(QLatin1String str, Qt::CaseSensitivity cs) const const
void clear()
bool doExecute(Context *c) override
Definition: action.cpp:136
qsizetype removeDuplicates()
qsizetype size() const const
QUrl uriFor(const QString &path={}, const QStringList &args={}, const ParamsMultiMap &queryValues={}) const
Definition: context.cpp:230
QString join(QStringView separator) const const
Request req
Definition: context.h:67
QString toString(QUrl::FormattingOptions options) const const
void setContentType(const QByteArray &type)
Definition: response.h:238
This class represents a Cutelyst Action.
Definition: action.h:35
int compare(QByteArrayView bv, Qt::CaseSensitivity cs) const const
A Cutelyst response.
Definition: response.h:28
The Cutelyst Context.
Definition: context.h:42
Cutelyst Controller base class.
Definition: controller.h:55
void append(QList::parameter_type value)
Automated REST method dispatching.
Definition: actionrest.h:15
Controller controller
Definition: context.h:76
bool doExecute(Context *c) override
Definition: actionrest.cpp:58
qsizetype removeAll(const AT &t)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString name() const noexcept
Definition: component.cpp:33
The Cutelyst namespace holds all public Cutelyst API.
bool execute(Component *code)
Definition: context.cpp:423
ActionREST(QObject *parent=nullptr)
Definition: actionrest.cpp:53
QString fromLatin1(QByteArrayView str)
QByteArray toLatin1() const const
QString mid(qsizetype position, qsizetype n) const const
QByteArray & body()
Definition: response.cpp:85
void sort(Qt::CaseSensitivity cs)
Action * actionFor(QStringView name) const
Definition: controller.cpp:259
void setBody(QIODevice *body)
Definition: response.cpp:103
Response * response() const noexcept
Definition: context.cpp:97
void setStatus(quint16 status) noexcept
Definition: response.cpp:72
ActionList actions() const noexcept
Definition: controller.cpp:269
void setHeader(const QByteArray &key, const QByteArray &value)