cutelyst  4.5.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
Cutelyst::Validator Class Reference

Validation processor for input data. More...

#include <Cutelyst/Plugins/Utils/Validator>

Public Types

enum  ValidatorFlag {
  NoSpecialBehavior , StopOnFirstError , FillStashOnError , NoTrimming ,
  BodyParamsOnly , QueryParamsOnly
}
 

Public Member Functions

 Validator (const char *translationContext=nullptr)
 
 Validator (std::initializer_list< ValidatorRule * > validators, const char *translationContext=nullptr)
 
 ~Validator ()
 
void addValidator (ValidatorRule *v)
 
void clear ()
 
ValidatorResult validate (Context *c, const ParamsMultiMap &parameters, ValidatorFlags flags=NoSpecialBehavior) const
 
ValidatorResult validate (Context *c, ValidatorFlags flags=NoSpecialBehavior) const
 

Static Public Member Functions

static void loadTranslations (Application *app)
 

Detailed Description

Validator can validate input data from the Context or a ParamsMultiMap using validation rules implemented as classes derived from ValidatorRule. As long as the Validator::StopOnFirstError flag is not set, all validations will be performed until the end. Validations will be performed in the order they were added on construction or via addValidator(). Any field can have any amount of validators. The Validator will take ownership of the added validator rules and will delete them on it’s own destruction.

Any validator requires at least the name of the field that should be validated. Some validators have additional mandatory parameters that have to be set. The ValidatorSame for example has a mandatory parameter to set the name of the other field to compare the values.

If you are using custom translatable error messages, you have to set the translation context on the Validator on the constructor if you use QT_TRANSLATE_NOOP, QT_TRANSLATE_NOOP3, QT_TRANSLATE_N_NOOP or QT_TRANSLATE_N_NOOP3 to mark your translation strings. The same translation context has than to be used with the custom strings for the validators. See ValidatorMessages for more information about translation of custom messages. If you use id based translations with QT_TRID_NOOP or QT_TRID_N_NOOP, you have to omit the context and leave it as nullptr.

Setting the FillStashOnError flag on the validate() function will add all error information as well as the not sensible (not containing the string "password" in the field name) input data to the stash if validation fails.

If only parameters from the request body or the request URL query should be taken into account by the validator, use the BodyParamsOnly or QueryParamsOnyly flag on the validate() function. If both are set, BodyParamsOnly takes precedence. If nothing is set, all parameters to validate will be taken from both, body and query.

Usage example

In Cutelyst 1.x validator rules were usable standalone without being part of a Validator object - even though they were never meant to be. This changed in Cutelyst 2.0.0 so that only the constructor and destructor of a ValiadtorRule are public anymore. However they now can be used more flexible by pointing them to other input fields or stash keys to get validation data like compare values. Some validator rules like the ValidatorEmail export their validation logic as static function so that it can be used standalone directly on a value without needing a Context.

Most validators will succeed if the input field is empty. You should use them together with one of the required validators if the input field is required. This approach is more flexible than having a simple switch in any validator. There are different validators to require a field that make it possible to have more complex requirements. You can find information about the behavior on empty input fields in the documenation of every validator rule. You can find some more general information at ValidatorRule and for sure in the documentation for every single validator rule. Information about writing your own validators that work with this concept can be found at ValidatorRule.

Validator will return a ValidatorResult after validation has been performed. The result can be used to check the validity of the input data. It will contain error messages from every failed ValidatorRule and a list of field names for which validation failed as well as the extracted values from the fields under validation. If there are no failed validations, the result will be valid, what you can check via ValidatorResult::isValid() or directly with an if statement.

#include <Cutelyst/Plugins/Utils/Validator> // includes the main validator
#include <Cutelyst/Plugins/Utils/Validators> // includes all validator rules
#include <Cutelyst/Plugins/Utils/ValidatorResult> // includes the validator result
void MyController::myform(Context *c)
{
if (c->req()->isPost()) {
// create a new static Validator with a set of rules and a translation context
static Validator v({
// this one will require the username to be present and not empty
new ValidatorRequired(QStringLiteral("username")),
// this one will require the username, if present (it has to be, see above),
// to have a length between 3 and 255 characters
new ValidatorBetween(QStringLiteral("username"), QMetaType::QString, 3, 255),
// username can be long, but we dont want have anything else than ASCII
// alpha-numeric characters, dashes and underscores in it
new ValidatorAlphaDash(QStringLiteral("username"), true),
// we also require an email address
new ValidatorRequired(QStringLiteral("email")),
// the email address should be valid, at least it should look like valid
// we are using a custom validation error message without a label
new ValidatorEmail(QStringLiteral("email"),
ValidatorEmail::Valid, // really strict validation
false, // we will not perform a DNS check
ValidatorMessages(nullptr,
QT_TRANSLATE_NOOP("MyController",
"The email address in the
Email field is not valid.")
)
),
// seems like we are building a registration form, so lets require a password
new ValidatorRequired(QStringLiteral("password")),
// the password should have a niminum length of 10 characters
new ValidatorMin(QStringLiteral("password"), QMetaType::QString, 10),
// the user should confirm the password in another field
// and here we are using a custom error message
new ValidatorConfirmed(QStringLiteral("password"),
ValidatorMessages(QT_TRANSLATE_NOOP("MyController",
"Password"),
QT_TRANSLATE_NOOP("MyController",
"Please enter the same
password again in the
confirmation field.")
)
);
}, QLatin1String("MyController"));
// ok, now we have all our validators in place - let the games begin
// we will set the FillStashOnError flag to automatically fill the context stash with
// error data and as you can see, we can directly use the ValidatorResult in an if
// statement, because of it's bool operator in this situation, because we are filling
// the stash directly in the Valiator
if (v.validate(c, FillStashOnError)) {
// ok everything is valid, we can now process the input data and advance to the
// next step for example
c->response()->redirect(uriFor("nextstep"));
// but what happens if the input data was not valid?
// because we set FillStashOnError, the Validator will automatically fill the stash
// with error information so that our user can enter them correclty now
}
}
c->setStash({QStringLiteral("template), QStringLiteral("myform.html")});
}
Validator(const char *translationContext=nullptr)
Definition: validator.cpp:18

Validator order

Each validator rule will return a ValidatorReturnType struct containing the validated value converted into a specific type as ValidatorReturnType::value. The Validator object will return a ValidatorResult object that contains all extracted values and error messages. For the values, the value set by the last validator rule will be used. If you have for example a ValidatorRequired and a ValidatorAfter for a date and time field the first coming ValidatorRequired would simply set a QString as result but the later coming ValdiatorAfter would overwrite this by a QDateTime.

Automatically filling the stash

If you set the FillStashOnError flag on the validate() function, the Validator will automatically fill the stash of the Context with error information and field values that are not sensible (field names that do not contain "password").

Beside the field values added with their field names, Validator will add two more entries to the stash:

Let's assume that a user enters the following values into the form fields from the example above:

  • username = detlef
  • email = detlef@irgendwo
  • password = schalke04
  • password_confirmation = schalke05

The validation will fail, because the email address is not completely valid (it uses a TLD as domain, what is allowed according to RFC5321, but we set the ValidatorEmail::Valid category as threshold for the validation, thas does not allow TLDs as domain part) and the password confirmation does not match the password.

Validator will add the following entries to the stash :

  • username: "detlef"
  • email: "detlef@irgendwo"
  • validationErrorStrings: ["The email address in the Email field is not valid.", "Please enter the same password again in the confirmation field."]
  • validationErrors: ["email":["The email address in the Email field is not valid."], "password":[""Please enter the same password again in the confirmation field.""]]

The sensible data of the password field is not part of the stash, but the other values can be used to prefill the form fields for the next attempt and can give the user some hints what was wrong.

Usage with Cutelee

The following example shows possible usage of the error data with Cutelee and the Bootstrap3 framework.

{% if validationErrorStrings.count %}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading">Errors in input data</h4>
<p>
<ul>
{% for errorString in validationErrorStrings %}
<li>{{ errorString }}</li>
{% endfor %}
</ul>
</p>
</div>
{% endif %}
<form>
<div class="form-group{% if validationErrors.email.count %} has-warning{% endif %}">
<label for="email">Email</label>
<input type="email" id="email" name="email" maxlength="255" class="form-control
{% if validationErrors.email.count %} form-control-warning{% endif %}"
placeholder="Email" aria-describedby="emailHelpBlock" required value="{{ email }}">
<small id="emailHelpBlock" class="form-text text-muted">The email address will be used
to send notifications and to restore lost passwords. Maximum length: 255</small>
</div>
</form>

Translations

Use Validator::loadTranslations(this) in your reimplementation of Application::init() if you are using the Validator plugin and want to use translated generic messages.

Definition at line 273 of file validator.h.

Member Enumeration Documentation

◆ ValidatorFlag

Flags that change the behavior of the Validator.

Enumerator
NoSpecialBehavior 

No special behavior, the default.

StopOnFirstError 

Will stop the validation process on the first failed validation.

FillStashOnError 

Will fill the context's stash with error information.

NoTrimming 

Will set trimBefore() to false on every validator. (default behavior is true)

BodyParamsOnly 

Will only check for parameters that are send in the request body. (since Cutelyst 2.0.0)

QueryParamsOnly 

Will only check for parameters that are part of the request URL query. (since Cutelyst 2.0.0)

Definition at line 279 of file validator.h.

Constructor & Destructor Documentation

◆ Validator() [1/2]

Validator::Validator ( const char *  translationContext = nullptr)
explicit

Constructs a new Validator object using translationContext.

For id based translations of custom messages translationContext has to be nullptr. See ValidatorMessages for more information about setting custom translatable messages.

Definition at line 18 of file validator.cpp.

◆ Validator() [2/2]

Validator::Validator ( std::initializer_list< ValidatorRule * >  validators,
const char *  translationContext = nullptr 
)
explicit

Constructs a new Validator object using the defined validators and translationContext.

The Validator object will take ownership of the validators and will destroy them on it’s own destruction.

For id based translations of custom messages translationContext has to be nullptr. See ValidatorMessages for more information about setting custom translatable messages.

Definition at line 23 of file validator.cpp.

◆ ~Validator()

Validator::~Validator ( )
default

Destroys the Validator object and all added ValidatorRule objects.

Member Function Documentation

◆ addValidator()

void Validator::addValidator ( ValidatorRule v)

Adds a new ValidatorRule v to the list of validators. On destruction of the Validator, all added rules will get destroyed, too.

Definition at line 122 of file validator.cpp.

◆ clear()

void Validator::clear ( )

Will clear the parameters and the used validators. ValidatorRule objects that have been added to the Validator will get destroyed.

Definition at line 31 of file validator.cpp.

◆ loadTranslations()

void Validator::loadTranslations ( Application app)
static

Loads the translations for the plugin.

Call this in your application’s init method when you want to use the generic error messages.

Definition at line 129 of file validator.cpp.

References Cutelyst::Application::loadTranslations().

◆ validate() [1/2]

ValidatorResult Validator::validate ( Context c,
const ParamsMultiMap parameters,
ValidatorFlags  flags = NoSpecialBehavior 
) const

◆ validate() [2/2]

Cutelyst::ValidatorResult Validator::validate ( Context c,
ValidatorFlags  flags = NoSpecialBehavior 
) const

Starts the validation process on Context c and returns a ValidatorResult.

Requests the input parameters from Context c and processes any validator added through the constructor or via addValidator() (unless Validator::StopOnFirstError is set). Returns a ValidatorResult that contains information about validation errors, if any. The result can be checked directly in if statements.

If Validator::FillStashOnError is set, it will fill the stash of Context with error data and not sensible input values.

Definition at line 41 of file validator.cpp.

References Cutelyst::Request::bodyParameters(), BodyParamsOnly, Cutelyst::Request::queryParameters(), QueryParamsOnly, Cutelyst::Context::req, and QMultiMap::unite().