Aller au contenu

validation: Bibliothèque de validation (Validation et filtrage des entrées) pour Lua et nginx-module-lua

Installation

Si vous n'avez pas encore configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

CentOS/RHEL 7 ou Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-validation

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-validation

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-validation v2.7 publié le 25 août 2017.


lua-resty-validation est une bibliothèque de validation et de filtrage par chaînage extensible pour Lua et OpenResty.

Hello World avec lua-resty-validation

local validation = require "resty.validation"

local valid, e = validation.number:between(0, 9)(5)  -- valid = true,  e = 5
local valid, e = validation.number:between(0, 9)(50) -- valid = false, e = "between"

-- Les validateurs peuvent être réutilisés
local smallnumber = validation.number:between(0, 9)
local valid, e = smallnumber(5)  -- valid = true,  e = 5
local valid, e = smallnumber(50) -- valid = false, e = "between"

-- Les validateurs peuvent faire du filtrage (c'est-à-dire modifier la valeur validée)
-- valid = true, s = "HELLO WORLD!"
local valid, s = validation.string.upper "hello world!"

-- Vous pouvez étendre la bibliothèque de validation avec vos propres validateurs et filtres...
validation.validators.capitalize = function(value) 
    return true, value:gsub("^%l", string.upper)
end

-- ... et ensuite l'utiliser
local valid, e = validation.capitalize "abc" -- valid = true,  e = "Abc"

-- Vous pouvez également valider plusieurs valeurs en groupe
local group = validation.new{
    artist = validation.string:minlen(5),
    number = validation.tonumber:equal(10)
}

local valid, fields, errors = group{ artist = "Eddie Vedder", number = "10" }

if valid then
  print("tous les champs du groupe sont valides")
else
  print(fields.artist.name,      fields.artist.error,
        fields.artist.valid,     fields.artist.invalid,
        fields.artist.input,     fields.artist.value, ,
        fields.artist.validated, fields.artist.unvalidated)
end

-- Vous pouvez même appeler fields pour obtenir un tableau simple de noms et de valeurs
-- (dans ce cas, tous les `nil` sont également supprimés)

-- Par défaut, cela ne retourne que les noms et valeurs des champs valides :
local data = fields()
local data = fields "valid"

-- Pour obtenir uniquement les noms et valeurs des champs invalides, appelez :
local data = fields "invalid"

-- Pour obtenir uniquement les noms et valeurs des champs validés, appelez (qu'ils soient valides ou non) :
local data = fields "validated"

-- Pour obtenir uniquement les noms et valeurs des champs non validés, appelez (qu'ils soient valides ou non) :
local data = fields "unvalidated"

-- Pour obtenir tout, appelez :
local data = fields "all"

-- Ou combinez :
local data = fields("valid", "invalid")

-- Cela ne s'arrête pas là. Vous pouvez également vouloir obtenir uniquement certains champs par leur nom.
-- Vous pouvez le faire en appelant (retourne un tableau) :
local data = data{ "artist" }

Validateurs et filtres intégrés

lua-resty-validation est livré avec plusieurs validateurs intégrés, et le projet est ouvert aux contributions de nouveaux validateurs.

Validateurs et filtres sans arguments

Les validateurs de type peuvent être utilisés pour valider le type de la valeur validée. Ces validateurs sont sans argument (appelez-les avec un point .) :

  • null ou ["nil"] (car nil est un mot-clé réservé en Lua)
  • boolean
  • number
  • string
  • table
  • userdata
  • func ou ["function"] (car function est un mot-clé réservé en Lua)
  • callable (soit une fonction soit une table avec la métaméthode __call)
  • thread
  • integer
  • float
  • file (io.type(value) == 'file')

Filtres de conversion de type :

  • tostring
  • tonumber
  • tointeger
  • toboolean

Autres filtres :

  • tonil ou tonull
  • abs
  • inf
  • nan
  • finite
  • positive
  • negative
  • lower
  • upper
  • trim
  • ltrim
  • rtrim
  • reverse
  • email
  • optional

Exemple

local validation = require "resty.validation"
local ok, e = validation.null(nil)
local ok, e = validation.boolean(true)
local ok, e = validation.number(5.2)
local ok, e = validation.string('Hello, World!')
local ok, e = validation.integer(10)
local ok, e = validation.float(math.pi)
local f = assert(io.open('filename.txt', "r"))
local ok, e = validation.file(f)

Validateurs et filtres de la fabrique de validation

La fabrique de validation se compose de différents validateurs et filtres utilisés pour valider ou filtrer la valeur (appelez-les avec deux-points :) :

  • type(t), valide que la valeur est de type t (voir les validateurs de type)
  • nil() ou ["null"](), vérifie que le type de valeur est nil
  • boolean(), vérifie que le type de valeur est boolean
  • number(), vérifie que le type de valeur est number
  • string(), vérifie que le type de valeur est string
  • table(), vérifie que le type de valeur est table
  • userdata(), vérifie que le type de valeur est userdata
  • func() ou ["function"](), vérifie que le type de valeur est function
  • callable(), vérifie que la valeur est callable (c'est-à-dire une fonction ou une table avec la métaméthode __call)
  • thread(), vérifie que le type de valeur est thread
  • integer(), vérifie que le type de valeur est integer
  • float(), vérifie que le type de valeur est float
  • file(), vérifie que le type de valeur est file (io.type(value) == 'file')
  • abs(), filtre la valeur et retourne la valeur absolue (math.abs)
  • inf(), vérifie que la valeur est inf ou -inf
  • nan(), vérifie que la valeur est nan
  • finite(), vérifie que la valeur n'est pas nan, inf ou -inf
  • positive(), valide que la valeur est positive (> 0)
  • negative(), valide que la valeur est négative (< 0)
  • min(min), valide que la valeur est au moins min (>=)
  • max(max), valide que la valeur est au plus max (<=)
  • between(min[, max = min]), valide que la valeur est entre min et max
  • outside(min[, max = min]), valide que la valeur n'est pas entre min et max
  • divisible(number), valide que la valeur est divisible par number
  • indivisible(number), valide que la valeur n'est pas divisible par number
  • len(min[, max = min]), valide que la longueur de la valeur est exactement min ou entre min et max (UTF-8)
  • minlen(min), valide que la longueur de la valeur est au moins min (UTF-8)
  • maxlen(max), valide que la longueur de la valeur est au plus max (UTF-8)
  • equals(equal) ou equal(equal), valide que la valeur est exactement quelque chose
  • unequals(equal) ou unequal(equal), valide que la valeur n'est pas exactement quelque chose
  • oneof(...), valide que la valeur est égale à l'un des arguments fournis
  • noneof(...), valide que la valeur n'est pas égale à aucun des arguments fournis
  • match(pattern[, init]), valide que la valeur correspond (string.match) au motif
  • unmatch(pattern[, init]), valide que la valeur ne correspond pas (string.match) au motif
  • tostring(), convertit la valeur en chaîne
  • tonumber([base]), convertit la valeur en nombre
  • tointeger(), convertit la valeur en entier
  • toboolean(), convertit la valeur en booléen (en utilisant not not value)
  • tonil() ou tonull(), convertit la valeur en nil
  • lower(), convertit la valeur en minuscules (le support UTF-8 n'est pas encore implémenté)
  • upper(), convertit la valeur en majuscules (le support UTF-8 n'est pas encore implémenté)
  • trim([pattern]), supprime les espaces (vous pouvez également utiliser un motif) à gauche et à droite
  • ltrim([pattern]), supprime les espaces (vous pouvez également utiliser un motif) à gauche
  • rtrim([pattern]), supprime les espaces (vous pouvez également utiliser un motif) à droite
  • starts(starts), vérifie si la chaîne commence par starts
  • ends(ends), vérifie si la chaîne se termine par ends
  • reverse, inverse la valeur (chaîne ou nombre) (UTF-8)
  • coalesce(...), si la valeur est nil, retourne la première valeur non-nil passée en arguments
  • email(), valide que la valeur est une adresse email
  • call(function), valide / filtre la valeur contre un validateur / filtre personnalisé en ligne
  • optional([default]), arrête la validation si la valeur est une chaîne vide "" ou nil et retourne true, et soit, default ou value

Validateurs de fabrique de validation conditionnels

Pour tous les validateurs de fabrique de validation, il existe une version conditionnelle qui valide toujours à vrai, mais où vous pouvez remplacer la valeur réelle en fonction de la validation du validateur d'origine. Eh bien, c'est plus facile à montrer qu'à dire :

local validation = require "resty.validation"

-- ok == true, value == "Oui, la valeur est nil"
local ok, value = validation:ifnil(
    "Oui, la valeur est nil",
    "Non, vous n'avez pas fourni de valeur nil")(nil)

-- ok == true, value == "Non, vous n'avez pas fourni de valeur nil"
local ok, value = validation:ifnil(
    "Oui, la valeur est nil",
    "Non, vous n'avez pas fourni de valeur nil")("non nil")

-- ok == true, value == "Oui, le nombre est entre 1 et 10"    
local ok, value = validation:ifbetween(1, 10,
    "Oui, le nombre est entre 1 et 10",
    "Non, le nombre n'est pas entre 1 et 10")(5)

-- ok == true, value == "Non, le nombre n'est pas entre 1 et 10"
local ok, value = validation:ifbetween(1, 10,
    "Oui, le nombre est entre 1 et 10",
    "Non, le nombre n'est pas entre 1 et 10")(100)

Les deux derniers arguments des validateurs de fabrique de validation conditionnels sont les valeurs truthy et falsy. Chaque autre argument est passé au validateur de fabrique de validation réel.

Validateurs de groupe

lua-resty-validation prend actuellement en charge quelques validateurs prédéfinis :

  • compare(comparison), compare deux champs et définit les champs comme invalides ou valides selon la comparaison
  • requisite{ fields }, au moins un des champs requis est nécessaire, même s'ils sont eux-mêmes optionnels
  • requisites({ fields }, number), au moins number de champs requis sont nécessaires (par défaut tous)
  • call(function), appelle une fonction de validation de groupe personnalisée (ou en ligne)
local ispassword = validation.trim:minlen(8)
local group = validation.new{
    password1 = ispassword,
    password2 = ispassword
}
group:compare "password1 == password2"
local valid, fields, errors = group{ password1 = "qwerty123", password2 = "qwerty123" }

local optional = validation:optional"".trim
local group = validation.new{
    text = optional,
    html = optional
}
group:requisite{ "text", "html" }
local valid, fields, errors = group{ text = "", html = "" }


local optional = validation:optional ""
local group = validation.new{
    text = optional,
    html = optional
}
group:requisites({ "text", "html" }, 2)
-- ou group:requisites{ "text", "html" }
local valid, fields, errors = group{ text = "", html = "" }


group:call(function(fields)
    if fields.text.value == "hello" then
        fields.text:reject "le texte ne peut pas être 'hello'"
        fields.html:reject "parce que le texte était 'hello', ce champ est également invalidé"
    end
end)

Vous pouvez utiliser les opérateurs relationnels Lua normaux dans le validateur de groupe compare :

  • <
  • >
  • <=
  • >=
  • ==
  • ~=

requisite et requisites vérifient si la valeur du champ est nil ou "" (chaîne vide). Avec requisite, si tous les champs spécifiés sont nil ou "", alors tous les champs sont invalides (à condition qu'ils ne soient pas eux-mêmes invalides), et si au moins un des champs est valide, alors tous les champs sont valides. requisites fonctionne de la même manière, mais vous pouvez définir le nombre de champs que vous souhaitez avoir une valeur qui n'est pas nil et pas une chaîne vide "". Cela fournit une validation conditionnelle dans le sens de :

  1. J'ai (deux ou plusieurs) champs
  2. Tous sont optionnels
  3. Au moins un / un nombre défini de champs doit être rempli mais je me fiche de lequel tant qu'il y a au moins un / un nombre défini de champs remplis

Validateurs d'arrêt

Les validateurs d'arrêt, comme optional, sont comme des validateurs normaux, mais au lieu de retourner true ou false comme résultat de validation OU comme valeur filtrée, vous pouvez retourner validation.stop. Cette valeur peut également être utilisée à l'intérieur de validateurs conditionnels et dans des validateurs qui prennent en charge les valeurs par défaut. Voici comment le validateur optional est implémenté :

function factory.optional(default)
    return function(value)
        if value == nil or value == "" then
            return validation.stop, default ~= nil and default or value
        end
        return true, value
    end
end

Ceux-ci sont à peu près équivalents :

-- Les deux retournent : true, "default" (ils arrêtent le traitement :minlen(10) sur les entrées nil et "")
local input = ""
local ok, val = validation.optional:minlen(10)(input)
local ok, val = validation:optional(input):minlen(10)(input)
local ok, val = validation:ifoneof("", nil, validation.stop(input), input):minlen(10)(input)

Filtrage de valeur et définition de la valeur à nil

La plupart des validateurs, qui ne filtrent pas la valeur, ne retournent que true ou false comme résultat. Cela signifie qu'il n'y a maintenant aucun moyen de signaler à resty.validation de définir réellement la valeur à nil. Donc, il y a une solution de contournement, vous pouvez retourner validation.nothing comme valeur, et cela changera la valeur en nil, par exemple, le validateur intégré tonil est en fait implémenté comme ceci (pseudo) :

function()
    return true, validation.nothing
end

Validateurs et filtres personnalisés (en ligne)

Parfois, vous pouvez simplement avoir des validateurs / filtres uniques que vous n'utilisez pas ailleurs, ou que vous voulez juste fournir rapidement un validateur / filtre supplémentaire pour un cas spécifique. Pour faciliter cela, nous avons introduit la méthode de fabrique call avec lua-resty-validation 2.4. Voici un exemple :

validation:call(function(value)
    -- maintenant validez / filtrez la valeur, et retournez les résultats
    -- ici nous retournons simplement false (c'est-à-dire que la validation échoue) 
    return false
end)("Vérifiez cette valeur"))

(bien sûr, cela n'a pas besoin d'être une fonction en ligne car en Lua, toutes les fonctions sont des citoyens de première classe et peuvent être passées comme paramètres)

Extensions de validateurs intégrés

Actuellement, lua-resty-validation prend en charge deux extensions ou plugins que vous pouvez activer :

  • resty.validation.ngx
  • resty.validation.tz
  • resty.validation.utf8

Ce sont des choses que vous pouvez consulter si vous souhaitez créer votre propre extension de validateur. Si vous le faites, et que vous pensez qu'elle pourrait être utile pour d'autres également, n'oubliez pas d'envoyer votre extension sous forme de pull-request pour inclusion dans ce projet, merci beaucoup, ;-).

Extension resty.validation.ngx

Comme son nom l'indique, cet ensemble d'extensions de validateurs nécessite OpenResty (ou le module Lua Nginx au moins). Pour utiliser cette extension, tout ce que vous devez faire est :

require "resty.validation.ngx"

Cela patchera les adaptateurs qu'il fournira dans resty.validation, et ceux-ci sont actuellement :

  • escapeuri
  • unescapeuri
  • base64enc
  • base64dec
  • crc32short
  • crc32long
  • crc32
  • md5

(il existe à la fois une version de fabrique et une version sans argument de ceux-ci)

Il y a également un match regex dans l'extension ngx qui utilise ngx.re.match, et md5 paramétré :

  • regex(regex[, options])
  • md5([bin])
Exemple
require "resty.validation.ngx"
local validation = require "resty.validation"
local valid, value = validation.unescapeuri.crc32("https://github.com/")
local valid, value = validation:unescapeuri():crc32()("https://github.com/")

Extension resty.validation.tz

Cet ensemble de validateurs et de filtres est basé sur la formidable bibliothèque luatz de @daurnimator, qui est une bibliothèque pour la manipulation du temps et de la date. Pour utiliser cette extension, tout ce que vous devez faire est :

require "resty.validation.tz"

Cela patchera les adaptateurs qu'il fournira dans resty.validation, et ceux-ci sont actuellement :

  • totimetable
  • totimestamp

(il existe à la fois une version de fabrique et une version sans argument de ceux-ci)

Les filtres totimestamp et totimetable fonctionnent très bien avec les champs d'entrée de date et de datetime HTML5. Comme son nom l'indique, totimetable retourne un timetable luatz et totimestamp retourne des secondes depuis l'époque unix (1970-01-01) sous forme de nombre Lua.

Exemple
require "resty.validation.tz"
local validation = require "resty.validation"
local valid, ts = validation.totimestamp("1990-12-31T23:59:60Z")
local valid, ts = validation.totimestamp("1996-12-19")

Extension resty.validation.utf8

Cet ensemble de validateurs et de filtres est basé sur la formidable bibliothèque utf8rewind de Quinten Lansu - une bibliothèque système écrite en C conçue pour étendre les fonctions de manipulation de chaînes par défaut avec le support du texte encodé en UTF-8. Elle nécessite mon wrapper LuaJIT FFI lua-resty-utf8rewind pour fonctionner. Lorsque les exigences mentionnées sont installées, le reste est facile. Pour utiliser cette extension, tout ce que vous devez faire est :

require "resty.validation.utf8"

Cela patchera les adaptateurs qu'il fournira dans resty.validation, et ceux-ci sont actuellement :

  • utf8upper
  • utf8lower
  • utf8title

(il existe à la fois une version de fabrique et une version sans argument de ceux-ci)

Il y a également quelques validateurs / filtres de fabrique :

  • utf8normalize(form)
  • utf8category(category)

Le utf8normalize normalise l'entrée UTF-8 dans l'un de ces formats de normalisation :

  • C (ou NFC)
  • D (ou NFD)
  • KC (ou NFKC)
  • KD (ou NFKD)

Le utf8category vérifie que la chaîne d'entrée est dans l'une des catégories suivantes (vous pouvez donc penser qu'elle a plusieurs validateurs intégrés pour travailler avec la validation de chaînes UTF-8) :

  • LETTER_UPPERCASE
  • LETTER_LOWERCASE
  • LETTER_TITLECASE
  • LETTER_MODIFIER
  • CASE_MAPPED
  • LETTER_OTHER
  • LETTER
  • MARK_NON_SPACING
  • MARK_SPACING
  • MARK_ENCLOSING
  • MARK
  • NUMBER_DECIMAL
  • NUMBER_LETTER
  • NUMBER_OTHER
  • NUMBER
  • PUNCTUATION_CONNECTOR
  • PUNCTUATION_DASH
  • PUNCTUATION_OPEN
  • PUNCTUATION_CLOSE
  • PUNCTUATION_INITIAL
  • PUNCTUATION_FINAL
  • PUNCTUATION_OTHER
  • PUNCTUATION
  • SYMBOL_MATH
  • SYMBOL_CURRENCY
  • SYMBOL_MODIFIER
  • SYMBOL_OTHER
  • SYMBOL
  • SEPARATOR_SPACE
  • SEPARATOR_LINE
  • SEPARATOR_PARAGRAPH
  • SEPARATOR
  • CONTROL
  • FORMAT
  • SURROGATE
  • PRIVATE_USE
  • UNASSIGNED
  • COMPATIBILITY
  • ISUPPER
  • ISLOWER
  • ISALPHA
  • ISDIGIT
  • ISALNUM
  • ISPUNCT
  • ISGRAPH
  • ISSPACE
  • ISPRINT
  • ISCNTRL
  • ISXDIGIT
  • ISBLANK
  • IGNORE_GRAPHEME_CLUSTER
Exemple
require "resty.validation.utf8"
local validation = require "resty.validation"
local valid, ts = validation:utf8category("LETTER_UPPERCASE")("TEST")

Extension resty.validation.injection

Cet ensemble de validateurs et de filtres est basé sur la formidable bibliothèque libinjection de Nick Galbreath - un analyseur de token SQL / SQLI / XSS. Il nécessite mon wrapper LuaJIT FFI lua-resty-injection pour fonctionner. Lorsque les exigences mentionnées sont installées, le reste est facile. Pour utiliser cette extension, tout ce que vous devez faire est :

require "resty.validation.injection"

Cela patchera les adaptateurs qu'il fournira dans resty.validation, et ceux-ci sont actuellement :

  • sqli, retourne false si une injection SQL a été détectée, sinon retourne true
  • xss, retourne false si une injection Cross-Site Scripting a été détectée, sinon retourne true
Exemple
require "resty.validation.injection"
local validation = require "resty.validation"
local valid, ts = validation.sqli("test'; DELETE FROM users;")
local valid, ts = validation.xss("test <script>alert('XSS');</script>")

API

Je ne vais pas ici entrer dans les détails de tous les différents validateurs et filtres car ils suivent tous la même logique, mais je vais montrer quelques manières générales dont cela fonctionne.

validation._VERSION

Ce champ contient une version de la bibliothèque de validation, par exemple, sa valeur peut être "2.5" pour la version 2.5 de cette bibliothèque.

validation booléenne, valeur/erreur...

Ces ... signifient la chaîne de validation. Cela est utilisé pour définir une seule chaîne de validateurs. Il n'y a pas de limite à la longueur de la chaîne. Cela retournera toujours un booléen (si la validation est valide ou non). La deuxième valeur de retour sera soit le nom du filtre qui n'a pas retourné true comme résultat de validation, soit la valeur filtrée.

local v = require "resty.validation"

-- Ce qui suit signifie, crée un validateur qui vérifie que l'entrée est :
-- 1. une chaîne
-- Si c'est le cas, alors supprime les espaces au début et à la fin de la chaîne :
-- 2. trim
-- Ensuite, vérifie que la longueur de la chaîne coupée est d'au moins 5 caractères (UTF-8) :
-- 3. minlen(5)
-- Et si tout va encore bien, convertit cette chaîne en majuscules
-- (le support UTF-8 n'est pas encore implémenté en majuscules) :
-- 4. upper
local myvalidator = v.string.trim:minlen(5).upper

-- Cet exemple retournera false et "minlen"
local valid, value = myvalidator(" \n\t a \t\n ")

-- Cet exemple retournera true et "ABCDE"
local valid, value = myvalidator(" \n\t abcde \t\n ")

Chaque fois que le validateur échoue et retourne false, vous ne devez pas utiliser la valeur retournée à d'autres fins que le rapport d'erreur. Ainsi, la chaîne fonctionne comme ça. lua-resty-validation ne tentera rien si vous spécifiez des chaînes qui ne seront jamais utilisées, telles que :

local v = require "resty.validation"
-- La valeur d'entrée ne peut jamais être à la fois une chaîne et un nombre en même temps :
local myvalidator = v.string.number:max(3)
-- Mais vous pourriez écrire cela comme ceci
-- (prendre l'entrée comme une chaîne, essayer de la convertir en nombre, et vérifier qu'elle est au plus 3) :
local myvalidator = v.string.tonumber:max(3)

Comme vous le voyez, c'est un moyen de définir des validateurs réutilisables uniques. Vous pouvez par exemple prédéfinir votre ensemble de chaînes de validateurs uniques de base et les stocker dans votre propre module à partir duquel vous pouvez réutiliser la même logique de validation dans différentes parties de votre application. Il est bon de commencer à définir des validateurs réutilisables uniques, puis de les réutiliser dans des validateurs de groupe.

Par exemple, disons que vous avez un module appelé validators :

local v = require "resty.validation"
return {
    nick     = v.string.trim:minlen(2),
    email    = v.string.trim.email,
    password = v.string.trim:minlen(8)
}

Et maintenant, vous avez une fonction register quelque part dans votre application :

local validate = require "validators"
local function register(nick, email, password)
    local vn, nick     = validate.nick(nick)
    local ve, email    = validate.email(email)
    local vp, password = validate.password(password)
    if vn and ve and vp then
        -- l'entrée est valide, faites quelque chose avec nick, email et password
    else
        -- l'entrée est invalide, nick, email et password contiennent les raisons d'erreur
    end
end

Cela devient rapidement un peu sale, et c'est pourquoi nous avons des validateurs de groupe.

validation.table.new([table de validateurs])

Cette fonction est où la validation de groupe commence. Disons que vous avez un formulaire d'inscription qui vous demande un nick, un email (deux fois), et un mot de passe (deux fois).

Nous allons réutiliser les validateurs uniques, définis dans le module validators :

local v = require "resty.validation"
return {
    nick     = v.string.trim:minlen(2),
    email    = v.string.trim.email,
    password = v.string.trim:minlen(8)
}

Maintenant, créons le validateur de groupe réutilisable dans le module forms :

local v        = require "resty.validation"
local validate = require "validators"

-- D'abord, nous créons des validateurs uniques pour chaque champ de formulaire
local register = v.new{
    nick      = validate.nick,
    email     = validate.email,
    email2    = validate.email,
    password  = validate.password,
    password2 = validate.password
}

-- Ensuite, nous créons des validateurs de groupe pour l'email et le mot de passe :
register:compare "email    == email2"
register:compare "password == password2"

-- Et enfin, nous retournons de ce module de formulaires

return {
    register = register
}

Maintenant, quelque part dans votre application, vous avez cette fonction register :

local forms = require "forms"
local function register(data)
    local valid, fields, errors = forms.register(data)
    if valid then
        -- l'entrée est valide, faites quelque chose avec les champs
    else
        -- l'entrée est invalide, faites quelque chose avec les champs et les erreurs
    end
end

-- Et vous pourriez l'appeler comme :

register{
    nick      = "test",
    email     = "[email protected]",
    email2    = "[email protected]",
    password  = "qwerty123",
    password2 = "qwerty123"
}

Le grand avantage des validateurs de groupe est que vous pouvez encoder en JSON les champs et le tableau d'erreurs et le retourner au client. Cela peut être utile lors de la construction d'une application à page unique et vous avez besoin de signaler des erreurs côté serveur au client. Dans l'exemple ci-dessus, la variable fields aura l'apparence suivante (valid serait vrai, et errors serait nil) :

{
    nick = {
        unvalidated = false,
        value = "test",
        input = "test",
        name = "nick",
        valid = true,
        invalid = false,
        validated = true
    },
    email = {
        unvalidated = false,
        value = "[email protected]",
        input = "[email protected]",
        name = "email",
        valid = true,
        invalid = false,
        validated = true
    },
    email2 = {
        unvalidated = false,
        value = "[email protected]",
        input = "[email protected]",
        name = "email2",
        valid = true,
        invalid = false,
        validated = true
    },
    password = {
        unvalidated = false,
        value = "qwerty123",
        input = "qwerty123",
        name = "password",
        valid = true,
        invalid = false,
        validated = true
    },
    password2 = {
        unvalidated = false,
        value = "qwerty123",
        input = "qwerty123",
        name = "password2",
        valid = true,
        invalid = false,
        validated = true
    }
}

C'est génial pour un traitement ultérieur et pour renvoyer les champs encodés en JSON à l'application Javascript côté client, mais généralement, c'est une structure trop lourde à envoyer à la couche backend. Pour obtenir un tableau clé-valeur simple, nous pouvons appeler ce tableau de champs :

local data = fields()

La variable data contiendra maintenant :

{
    nick = "test",
    email = "[email protected]",
    email2 = "[email protected]",
    password = "qwerty123",
    password2 = "qwerty123"
}

Maintenant, c'est quelque chose que vous pouvez envoyer par exemple dans Redis ou quelle que soit la couche de base de données (abstraction) que vous avez. Mais, eh bien, cela ne s'arrête pas là, si par exemple votre couche de base de données s'intéresse uniquement à nick, email et password (par exemple, supprimer ces doublons), vous pouvez même appeler le tableau data :

local realdata = data("nick", "email", "password")

Le realdata contiendra maintenant :

{
    nick = "test",
    email = "[email protected]",
    password = "qwerty123"
}

field:accept(value)

Pour le champ, vous pouvez appeler accept qui fait ceci :

self.error = nil
self.value = value
self.valid = true
self.invalid = false
self.validated = true
self.unvalidated = false

field:reject(error)

Pour le champ, vous pouvez appeler reject qui fait ceci :

self.error = error
self.valid = false
self.invalid = true
self.validated = true
self.unvalidated = false

string field:state(invalid, valid, unvalidated)

Appeler state sur le champ est génial lorsque vous intégrez les résultats de validation dans par exemple un modèle HTML, tel que lua-resty-template. Voici un exemple utilisant lua-resty-template :

<form method="post">
    <input class="{{ form.email:state('invalid', 'valid') }}"
            name="email"
            type="text"
            placeholder="Email"
            value="{{ form.email.input }}">
    <button type="submit">Rejoindre</button>
</form>

Ainsi, en fonction de l'état du champ email, cela ajoutera une classe à l'élément d'entrée (par exemple, rendant la bordure de l'entrée rouge ou verte par exemple). Nous ne nous soucions pas de l'état non validé (par exemple, lorsque l'utilisateur a d'abord chargé la page et le formulaire).

Changements

Les changements de chaque version de ce module sont enregistrés dans le fichier Changes.md.

Voir aussi

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-validation.