cutelyst 5.0.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validator.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2017-2025 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validator_p.h"
7
8#include <Cutelyst/application.h>
9#include <Cutelyst/context.h>
10#include <Cutelyst/request.h>
11
12#include <QLoggingCategory>
13
14using namespace Cutelyst;
15using namespace Qt::Literals::StringLiterals;
16
17Q_LOGGING_CATEGORY(C_VALIDATOR, "cutelyst.utils.validator", QtWarningMsg)
18
19Validator::Validator(const char *translationContext)
20 : d_ptr(new ValidatorPrivate(translationContext))
21{
22}
23
24Validator::Validator(std::initializer_list<ValidatorRule *> validators,
25 const char *translationContext)
26 : d_ptr(new ValidatorPrivate(validators, translationContext))
27{
28}
29
30Validator::~Validator() = default;
31
33{
34 Q_D(Validator);
35 if (!d->validators.empty()) {
36 qDeleteAll(d->validators.begin(), d->validators.end());
37 d->validators.clear();
38 }
39}
40
42{
43 ValidatorResult result;
44
45 Q_ASSERT(c);
46
47 ParamsMultiMap params;
48 if (flags.testFlag(BodyParamsOnly)) {
49 params = c->req()->bodyParameters();
50 } else if (flags.testFlag(QueryParamsOnly)) {
51 params = c->req()->queryParameters();
52 } else {
53 params = c->req()->queryParameters();
54 params.unite(c->req()->bodyParameters());
55 }
56
57 result = validate(c, params, flags);
58
59 return result;
60}
61
63 Validator::validate(Context *c, const ParamsMultiMap &params, ValidatorFlags flags) const
64{
65 ValidatorResult result;
66
67 Q_ASSERT(c);
68 Q_D(const Validator);
69
70 if (d->validators.empty()) {
71 qCWarning(C_VALIDATOR) << "Validation started with empty validator list.";
72 return result;
73 }
74
75 if (params.empty()) {
76 qCWarning(C_VALIDATOR) << "Validation started with empty parameters.";
77 }
78
79 const bool stopOnFirstError = flags.testFlag(StopOnFirstError);
80 const bool noTrimming = flags.testFlag(NoTrimming);
81
82 for (ValidatorRule *v : d->validators) {
83
84 if (noTrimming) {
85 v->setTrimBefore(false);
86 }
87
88 const ValidatorReturnType singleResult = v->validate(c, params);
89
90 if (singleResult.extra.isValid()) {
91 result.addExtra(v->field(), singleResult.extra);
92 }
93
94 if (singleResult) {
95 result.addValue(v->field(), singleResult.value);
96 } else {
97 result.addError(v->field(), singleResult.errorMessage);
98 if (stopOnFirstError) {
99 break;
100 }
101 }
102 }
103
104 if (!result && flags.testFlag(FillStashOnError)) {
105 c->setStash(u"validationErrorStrings"_s, result.errorStrings());
106 c->setStash(u"validationErrors"_s, QVariant::fromValue(result.errors()));
107
108 for (const auto &[key, value] : params.asKeyValueRange()) {
109 if (!key.contains(u"password"_s, Qt::CaseInsensitive)) {
110 c->setStash(key, value);
111 }
112 }
113 }
114
115 return result;
116}
117
119{
121 auto cb = avr.callback;
122
123 Q_D(const Validator);
124
125 ParamsMultiMap params;
126 if (flags.testFlag(BodyParamsOnly)) {
127 params = c->req()->bodyParameters();
128 } else if (flags.testFlag(QueryParamsOnly)) {
129 params = c->req()->queryParameters();
130 } else {
131 params = c->req()->queryParameters();
132 params.unite(c->req()->bodyParameters());
133 }
134
135 auto async = new AsyncValidator{c};
136 QObject::connect(async,
137 &AsyncValidator::finished,
138 [cb](const Cutelyst::ValidatorResult &result) { cb(result); });
139 async->start(d->validators, flags, params);
140
141 return avr;
142}
143
145 Validator::coValidate(Context *c, const ParamsMultiMap &params, ValidatorFlags flags) const
146{
148 auto cb = avr.callback;
149
150 Q_D(const Validator);
151
152 auto async = new AsyncValidator{c};
153 QObject::connect(async,
154 &AsyncValidator::finished,
155 [cb](const Cutelyst::ValidatorResult &result) { cb(result); });
156 async->start(d->validators, flags, params);
157
158 return avr;
159}
160
162{
163 Q_D(Validator);
164 v->setTranslationContext(d->translationContext);
165 d->validators.push_back(v);
166}
167
169{
170 app->loadTranslations(u"plugin_utils_validator"_s);
171}
172
173void AsyncValidator::start(const std::vector<ValidatorRule *> &validators,
174 Validator::ValidatorFlags flags,
175 const ParamsMultiMap &params)
176{
177 if (validators.empty()) {
178 qCWarning(C_VALIDATOR) << "Validation started with empty validator list.";
179 Q_EMIT finished(m_result);
180 return;
181 }
182
183 if (params.empty()) {
184 qCWarning(C_VALIDATOR) << "Validation started with empty parameters.";
185 }
186
187 m_params = params;
188
189 for (auto validator : validators) {
190 m_validators.enqueue(validator);
191 }
192
193 m_stopOnFirstError = flags.testFlag(Validator::StopOnFirstError);
194 m_noTrimming = flags.testFlag(Validator::NoTrimming);
195 m_fillStashOnError = flags.testFlag(Validator::FillStashOnError);
196
197 QMetaObject::invokeMethod(this, "doValidation", Qt::DirectConnection);
198}
199
200void AsyncValidator::doValidation()
201{
202 if (m_validators.empty() || m_cancelValidation) {
203 Q_EMIT finished(m_result);
204 return;
205 }
206
207 auto v = m_validators.dequeue();
208
209 if (m_context.isNull()) {
210 qCCritical(C_VALIDATOR)
211 << "Cutelyst context object was destroyed while performing validation";
212 m_result.addError(
213 v->field(),
214 QStringLiteral("Cutelyst context object was destroyed while performing validation."));
215 Q_EMIT finished(m_result);
216 return;
217 }
218
219 v->validateCb(m_context.get(), m_params, [this, v](ValidatorReturnType &&singleResult) {
220 if (singleResult.extra.isValid()) {
221 m_result.addExtra(v->field(), singleResult.extra);
222 }
223
224 if (singleResult) {
225 m_result.addValue(v->field(), singleResult.value);
226 } else {
227 m_result.addError(v->field(), singleResult.errorMessage);
228 if (m_stopOnFirstError) {
229 m_cancelValidation = true;
230 }
231 }
232 doValidation();
233 });
234}
The Cutelyst application.
Definition application.h:66
void loadTranslations(const QString &filename, const QString &directory={}, const QString &prefix={}, const QString &suffix={})
Coroutine awaitable for ValidatorResult.
The Cutelyst Context.
Definition context.h:42
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:213
Request * req
Definition context.h:66
ParamsMultiMap bodyParameters() const
Definition request.cpp:220
ParamsMultiMap queryParameters() const
Definition request.cpp:256
Provides information about performed validations.
void addExtra(const QString &field, const QVariant &extra)
void addValue(const QString &field, const QVariant &value)
QHash< QString, QStringList > errors() const noexcept
void addError(const QString &field, const QString &message)
QStringList errorStrings() const
Base class for all validator rules.
Validation processor for input data.
Definition validator.h:309
AwaitedValidatorResult coValidate(Context *c, ValidatorFlags flags=NoSpecialBehavior) const
ValidatorResult validate(Context *c, ValidatorFlags flags=NoSpecialBehavior) const
Definition validator.cpp:41
Validator(const char *translationContext=nullptr)
Definition validator.cpp:19
void addValidator(ValidatorRule *v)
static void loadTranslations(Application *app)
The Cutelyst namespace holds all public Cutelyst API.
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
auto asKeyValueRange() &
bool empty() const const
QMultiMap< Key, T > & unite(QMultiMap< Key, T > &&other)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
CaseInsensitive
DirectConnection
QVariant fromValue(T &&value)
bool isValid() const const
Contains the result of a single input parameter validation.