Convalidare un indirizzo e-mail è un requisito classico di quasi tutte le applicazioni web. La maggior parte dei moderni web framework client-side e server-side prevede dei metodi nativi per assolvere a questa esigenza: tuttavia, come molti sviluppatori web sanno fin troppo bene, le metodologie utilizzate per la convalida degli indirizzi e-mail non sempre restituiscono il medesimo risultato - e non sempre ciò che è considerato "valido" per loro corrisponde al risultato desiderato per il nostro caso specifico.
Prendiamo ad esempio il caso di questi indirizzi e-mail:
- admin@localhost
- a@a
- Abc\@def@example.com
- Fred\ Bloggs@example.com
- Joe.\\Blow@example.com
- "Abc@def"@example.com
- "Fred Bloggs"@example.com
- customer/department=shipping@example.com
- $A12345@example.com
- !def!xyz%abc@example.com
- _somename@example.com
Per quanto strano possa sembrare, tutti gli indirizzi e-mail di cui sopra sono "validi": o perlomeno lo erano, secondo quanto previsto dalla RFC 2822 (sezione 3.4.1), fino a quando non è stata resa obsoleta dalla successiva RFC 5322. Tuttavia, anche RFC 5322 considera " validi" una vasta tipologia di indirizzi e-mail che utilizzano una sintassi che oggi viene comunemente considerata troppo rigida (prima del carattere "@"), troppo vaga (dopo il carattere "@") e troppo permissiva (in quanto consente l'inserimento di commenti, spazi bianchi e parole o persino frasi "virgolettate").
Ecco qualche esempio di indirizzo email considerato "valido" secondo quanto previsto dalla RFC 5322:
- joe.blow@[IPv6:2001:db8::1]
- joe.blow(comment)@example.com
- joe.blow(comment)@(another comment with spaces)example.com
- "Joe..Blow"@example.com
Come possiamo vedere, anche queste specifiche sono ahimé ancora piuttosto lontane da qualsiasi possibile utilizzo concreto, a meno di non voler rischiare che il proprio sito "accetti" una serie di indirizzi e-mail difficilmente utilizzabili all'atto pratico.
Una soluzione "quasi perfetta" è arrivata con l'introduzione degli HTML living standards, nelle quali compare per la prima volta una "willful violation" (violazione intenzionale) della RFC 5322 compiuta proprio per giungere a criteri di validazione che potessero superare i problemi di cui sopra. Questa nuova tecnica di validazione è stata opportunamente riassunta mediante una espressione regolare "ufficiale", rilasciata all'interno del documento in formato compatibile con JavaScript e Perl:
1 |
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ |
La RegEx di cui sopra consente finalmente di eliminare la maggior parte degli "indirizzi anomali" che abbiamo riportato in questo articolo: non a caso è utilizzata da una pluralità di siti, servizi e applicazioni web (io stesso ne ho fatto largo uso in passato). Tuttavia, persino questa soluzione non è esente da problemi, in quanto considera ancora validi gli indirizzi e-mail "dot-less", ovvero senza il punto all'interno del dominio. Qualche esempio:
- admin@localhost
- abc@cba
A ben vedere, non si tratta di un vero e proprio problema della RegEx: gli indirizzi di cui sopra - così come tutti gli indirizzi che prevedano un dominio "dot-less" - sono infatti indiscutibilmente validi, ed esistono certamente molti scenari in cui è opportuno considerarli tali. Ad esempio, se l'applicazione o il servizio web che stiamo sviluppando prevede l'invio di e-mail a una intranet, dove spesso il dominio interno è di tipo "dotless". Tuttavia, considerare "validi" questo tipo di indirizzi potrebbe creare non pochi problemi nel caso di applicazioni o servizi web pensate esclusivamente per essere utilizzate da utenti esterni: in quei casi, considerare "non valido" qualsiasi indirizzo "dot-less" può essere una scelta preferibile.
Questo è il motivo che mi ha spinto a implementare EmailValidator, una classe ad-hoc scritta in C# che può essere utilizzata per convalidare gli indirizzi di posta elettronica con o senza punti.
Ecco il codice sorgente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public static class EmailValidator { /// <summary> /// ref.: https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address (HTML5 living standard, willful violation of RFC 3522) /// </summary> public static readonly string EmailValidation_Regex = @"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"; public static readonly Regex EmailValidation_Regex_Compiled = new Regex(EmailValidation_Regex, RegexOptions.IgnoreCase); public static readonly string EmailValidation_Regex_JS = $"/{EmailValidation_Regex}/"; /// <summary> /// Checks if the given e-mail is valid using various techniques /// </summary> /// <param name="email">The e-mail address to check / validate</param> /// <param name="useRegEx">TRUE to use the HTML5 living standard e-mail validation RegEx, FALSE to use the built-in validator provided by .NET (default: FALSE)</param> /// <param name="requireDotInDomainName">TRUE to only validate e-mail addresses containing a dot in the domain name segment, FALSE to allow "dot-less" domains (default: FALSE)</param> /// <returns>TRUE if the e-mail address is valid, FALSE otherwise.</returns> public static bool IsValidEmailAddress(string email, bool useRegEx = false, bool requireDotInDomainName = false) { var isValid = useRegEx // see RegEx comments ? email is not null && EmailValidation_Regex_Compiled.IsMatch(email) // ref.: https://stackoverflow.com/a/33931538/1233379 : new EmailAddressAttribute().IsValid(email); if (isValid && requireDotInDomainName) { var arr = email.Split('@', StringSplitOptions.RemoveEmptyEntries); isValid = arr.Length == 2 && arr[1].Contains("."); } return isValid; } } |
Come si può vedere osservando il codice di cui sopra, il processo di convalida si basa sulla funzione statica IsValidEmailAddress, che accetta i seguenti parametri:
- email : l'indirizzo email da controllare/convalidare
- useRegEx : TRUE per utilizzare la validazione e-mail standard HTML5 RegEx, FALSE per utilizzare il validatore integrato fornito da .NET (predefinito: FALSE)
- requireDotInDomainName : TRUE per convalidare solo gli indirizzi e-mail contenenti un punto nel segmento del nome di dominio, FALSE per consentire domini "senza punti" (predefinito: FALSE)
Il codice di cui sopra è stato rilasciato sotto licenza MIT, il che significa che chiunque è libero di usarlo per qualsiasi progetto o come riferimento per sviluppare una funzione di convalida alternativa.
Conclusioni
Per il momento è tutto: se questo articolo vi è stato utile, non dimenticate di lasciarci un feedback nella sezione "commenti".