Polub bloga na fejsie!

Walidacja e-mail za pomocą wyrażeń regularnych

2

Zapraszam na kolejny wpis gościnny na moim blogu! Jego temat – walidacja e-mail za pomocą Regex’ów – to na pewno coś co warto znać. Autorem posta jest Tomek Sochacki, z wykształcenia inżynier budownictwa, a obecnie programista, specjalizujący się w projektowaniu relacyjnych systemów bazo-danowych, pasjonat JavaScript oraz RegExp. Bloga Tomka możesz znaleźć tutaj.

W artykule omówimy zastosowanie wyrażeń regularnych w celu walidacji adresu e-mail, np. w formularzu kontaktowym, w czasie rejestracji użytkownika, w aplikacjach CRM itp.

Na wstępie należy jednak pamiętać o jednej, ważnej zasadzie: Walidacja danych wprowadzanych przez użytkownika ZAWSZE, ale to bez wyjątku ZAWSZE musi odbywać się na serwerze. Walidacja po stronie klienta jest dobrym rozwiązaniem gdyż pozwala szybko poinformować użytkownika o ewentualnych błędach bez konieczności komunikacji z serwerem (Ajax) ale pamiętajmy, że kod JavaScript może zostać zmodyfikowany, dlatego walidacja ta stanowi jedynie dodatek do właściwej walidacji serwerowej.

Istnieje wiele skryptów z gotowymi metodami do walidacji e-maili, w tym również dodatki do biblioteki jQuery. My jednak zajmiemy się samodzielną walidacją z wykorzystaniem wyrażeń regularnych aby móc w pełni świadomie kontrolować cały proces analizy adresu e-mail.

Proponowane we wpisie wyrażenie regularne nie jest zaprzeczeniem gotowych bibliotek typu jQuery Validate. Zapewnia ono jednak pełną kontrolę nad procesem walidacji zgodnie z przyjętymi przez programistę zasadami (a nie ogólnymi standardami). Jego niewątpliwą zaletą jest to, że nie wymaga podciągania kolejnej biblioteki do sprawdzenia jednego czy dwóch pól formularza.

Co to właściwie znaczy „poprawny” adres e-mail?

Zasady budowy adresów e-mail są zawarte w standardach RFC i najogólniej mówiąc można przyjąć, że poprawny adres składa się z:

  • dużych i małych liter alfabetu łacińskiego [A-Za-z],
  • cyfr [0-9],
  • znaku kropki (co najmniej jednego),
  • znaku „@” (dokładnie jednego),
  • znaków myślnika i podkreśleń dolnych.

Standard RFC dopuszcza jeszcze co prawda stosowanie w adresach znaków specjalnych jak !#$%… tylko pytanie, czy na pewno chcemy dopuszczać takie adresy jako poprawne?

W praktyce nikt nie założy sobie świadomie adresu zawierającego znaki specjalne gdyż po pierwsze, jest on trudny do zapamiętania, a po drugie nie przejdzie walidacji w wielu witrynach.

W moich przykładach uznaję zatem, że poprawny adres to taki, który zawiera tylko ww. elementy (litery, cyfry, kropki, @, myślnik, podkreślenie). Ponadto należy zwrócić uwagę na jeszcze jeden drobny szczegół. Adres e-mail (a dokładniej nazwa użytkownika) musi zaczynać się od litery [A-Za-z] lub cyfry – na początku nie może znajdować się żaden inny znak.

Zasady tworzenia nazw domen

Nazwa domeny może składać się z liter alfabetu łacińskiego „[A-Za-z]”, cyfr „[0-9]” oraz znaku minusa „-„. Zauważ, że nie występuje tutaj znak podkreślenia, dlatego nie można w tym miejscu używać skróconej formy regexp: \w.

Obecnie w nazwach domen można również stosować znaki diaktryczne, czyli w naszym wypadku tzw. „polskie znaki”. Pytanie jednak czy na pewno chcemy dopuścić ich obecność w adresie e-mail? Osobiście uważam, że w 99,9% przypadków wpisanie przez użytkownika w adresie polskiego znaku diaktrycznego będzie wynikiem błędu edytorskiego. Uważam zatem, że choć adres alicja.kozłowska@kórnik.pl byłby teoretycznie poprawny, to w praktyce jest to błąd we wpisywaniu adresu, który w rzeczywistości nie zawiera polskich znaków.

Pamiętajmy więc, aby znaleźć rozsądny kompromis pomiędzy faktycznymi zasadami tworzenia danych (standard RFC dla e-mail), a możliwymi przypadkowymi błędami użytkownika.

Na początek opracujmy zestaw testów

Tworząc testy dla wyrażenia regularnego należy pamiętać o wielu aspektach jak rodzaje znaków, znaki specjalne, wielkość liter, kwantyfikatory powtórzeń dopasowania, granice dopasowania itp.

Na potrzeby dalszych rozważań stworzymy sobie tablice zawierające kilka wersji adresów e-mail zarówno poprawnych jak i błędnych, a następnie przeiterujemy po tablicach wywołując dla każdego z adresów metodę RegExp.prototype.test().

Walidacja e-mail – pierwsze wyrażenie regularne

POLUB BLOGA NA FACEBOOKU!

Chcesz być na bieżąco informowany o nowościach na blogu oraz innych ciekawych treściach? Polub fanpage bloga na Facebooku!

Zaczniemy od wzorca regexp, który dość często przewija się w wielu poradnikach i książkach poświęconych językowi JavaScript. Jest ono krótkie i stosunkowo proste w zrozumieniu lecz niestety posiada również pewne wady:

Oto wyniki dla powyższych testów:

Analiza testów dla pierwszego wzorca RegExp

Jak widać testy adresów poprawnych przeszły prawidłowo, akceptując każdy z podanych adresów. Pewien problem pojawia się jednak w testach badających błędne adresy e-mail.

Wyrażenie regularne nie przestrzega dwóch zasad tworzenia adresów e-mail i nazw domen:

  • Nazwa użytkownika w adresie (część przed „@”) musi zaczynać się od litery [A-Za-z] lub cyfry [0-9], więc zapis \w jest błędny, gdyż zezwala na stosowanie na początku adresu również znaku podkreślenia. Ponad to dopuszczamy jednocześnie użycie na początku znaku myślnika, co również jest niepoprawne.
  • W nazwie domeny zezwalamy, aby zaczynała się ona od myślnika i dolnego podkreślenia co również jest błędem. Domena może natomiast zaczynać się cyfrą.

Zmodyfikowane wyrażenie sprawdzające e-mail

Osobiście stosuję nieco inne wyrażenie do sprawdzania poprawności adresów e-mail, którego składnia jest następująca:

Omówmy dokładnie zmiany jakie wprowadziliśmy we wcześniejszej wersji wzorca regexp.

Po pierwsze wprowadziłem ograniczenia w zakresie pierwszych znaków w nazwie użytkownika oraz w nazwie domeny. I tak nazwa użytkownika musi zaczynać się od co najmniej jednej litery lub cyfry [A-Za-z0-9] (w przykładzie jest [a-z\d] ale zwróć uwagę na końcową flagę i). Nazwa domeny z kolei musi zaczynać się od litery bądź cyfry [A-Za-z0-9] i nie może zaczynać się myślnikiem.

Kolejna zmiana to ograniczenie ilości subdomen oddzielanych kropką. Ograniczenie to nie wynika ze specyfikacji RFC lecz z moich własnych założeń. Wyszedłem w tym momencie z założenia, że sześcioczłonowa nazwa domeny (od 1 do 5 + końcówka, np. „.pl”) to maksymalna ilość, jaka może wystąpić w ogólnie przyjętych poprawnych adresach. Zakładam więc, że większa ilość subdomen może oznaczać błąd użytkownika wprowadzającego adres, dlatego odrzucam go jako nieprawidłowy. Jest to kwestia moich własnych założeń, które jeśli chcesz możesz zmienić lub w ogóle zrezygnować z ograniczania ilości subdomen.

Zakładam również, że końcówka domeny musi być złożona z co najmniej dwóch i maksymalnie sześciu znaków.

Zastosowałem także tzw. grupowanie nieprzechwytujące dopasowując nazwę domeny  – symbol ?:. Nie jest to konieczne, ale uważam to za dobrą praktykę jeśli nie potrzebujemy operować na znalezionych dopasowaniach, np. w metodzie String.prototype.replace().

Testujemy drugą wersję wzorca RegExp

Ok, przetestujmy więc drugą wersję wyrażenia regularnego na tych samych adresach e-mail:

Tym razem wszystkie nasze testy wychodzą prawidłowo, czyli dla e-maili poprawnych zwracana jest wartość true, a dla błędnych wartość false.

Walidacja e-mail – podsumowanie

Zastanówmy się jednak czy to na pewno koniec testów? Otóż warto jeszcze wspomnieć o pewnym szczególe. Jeśli w polu input (typu „text” lub „email”) wpiszemy ”   tomek@drogimex.pl  ” to pobierając później wartość value elementu DOM będziemy mieli również początkowe i końcowe spacje (tzw. białe znaki).

W adresie e-mail nie może być spacji, lecz uważam, że warto przepuścić wartość z „input’a” przez metodę String.prototype.trim(), która usunie początkowe i końcowe białe znaki i dopiero taką wersję poddać walidacji RegExp. Dlaczego tak? Otóż wychodzę w tym wypadku z założenia, że spacje na początku (na końcu raczej się nie zdarzają) wynikają z omyłki użytkownika i nie traktuje on spacji jako elementu wprowadzanego adresu e-mail. Wyjdźmy zatem naprzeciw takim sytuacjom i usuńmy te nadmiarowe spacje. Jeśli tak przetworzony adres przejdzie testy walidacji to adres (już bez spacji) można przesłać na serwer… w celu drugiej – właściwej walidacji.

Osobiście uważam, że wyrażenia regularne to bardzo dobry sposób walidacji różnego rodzaju danych, jednakże zawsze należy tworzyć je z rozsądkiem aby znaleźć kompromis między wydajnością RegExp, a faktycznymi możliwymi błędami użytkownika.

I na koniec mała uwaga praktyczna… Zawsze dokładnie przeanalizuj swoje testy. Użytkownicy mają tak wiele pomysłów jak źle coś wpisać, że w praktyce nie sposób chyba wyłapać wszystkie możliwości. Proste formularze na stronach www to jeszcze nie problem, ale gdy wchodzimy w bardziej zaawansowane aplikacje, gdzie użytkownik wprowadza kilkanaście danych to na prawdę mogą dziać się cuda… Powinniśmy w miarę możliwości elegancko obsłużyć błędy i poinformować użytkownika o popełnionych błędach.

Czasami warto również spojrzeć na niektóre sprawy z boku, jak chociażby na kwestię dopuszczenia polskich liter w adresach e-mail. Byłoby to poprawne technicznie i zgodne ze specyfikacjami, ale pytanie, ile takich adresów będą stanowiły adresy faktycznie zawierające znaki diaktryczne, a ile z nich to będą po prostu błędnie wpisane e-maile?

REACT, REDUX, REACT-ROUTER - KURSY ON-LINE

Chcesz od podstaw poznać tajniki React, Redux oraz react-router? Zapraszam do moich szkoleń on-line:

Przejdź do szkoleń

Uwaga! Obecnie trwa przedsprzedaż kursów - premiera 1 sierpnia 2017!

  • Świetnie uargumentowany problem. Artykuł mi się podoba.

    Podobnie jak autor artykułu, jestem fanem RegExp-ów, jednak
    zastanawiam się, dlaczego nie użyć natywnej walidacji przeglądarki
    pola o typie email, np. ?
    Ze względu na ograniczone wsparcie w przeglądarkach?

    Czy może sprawa rozchodzi o to, że natywna walidacja przeglądarki nie jest wiarygodna?
    Testując w polu input[type="email"] np. ciąg znaków: piotr@7balls to nie jest on traktowany jako błąd,
    a jednak powinien, bo to nie jest poprawny adres email 🙂

  • Stosowanie wyrażeń regularnych ma tę zaletę, o czym wspomniano w artykule, że mamy pełną kontrolę nad sposobem walidacji danych. W ostatnim czasie coraz częściej spotykam na stronach jQuery validate, ale gdybyśmy zagłębili się w kod tej biblioteki to znajdziemy metodę email, która sprawdza adres za pomocą poniższego wyrażenia regularnego:

    Widać zatem, że zezwala ona na stosowanie wielu znaków specjalnych co ja w moim regexp uznaję za błąd. Nie należy jednak bezpośrednio porównywać obu rozwiązań (mojego i jQuery), gdyż jeśli tworzymy regexp samodzielnie to robimy to na własne potrzeby (lub dla projektu, którego jesteśmy autorem). JQuery validate to biblioteka ogólnodostępna i sprawdza ona zgodność adresu ze standardami RFC. Jest to jak najbardziej poprawne zachowanie, gdyż tworząc tak ogólne biblioteki należy przestrzegać określonych zasad. Ja uważam jednak, że warto zawsze pomyśleć nie tylko o standardach (np. RFC) ale również o możliwych błędach użytkownika.
    Co do walidacji HTML5 to pierwsze nie mamy pewności w jaki sposób jest ona realizowana, gdyż zależy od przeglądarki i nie sprawdza ona zgodnie ze standardami RFC. Chyba, że zastosujemy pewien dodatkowy zabieg, a mianowicie wykorzystamy własne wyrażenie sprawdzające:

    Pozostaje jeszcze problem obsługi ww. elementów w przeglądarce. W przypadku „tradycyjnej” metody z walidacją np. metodą RegExp.prototype.test nie ma tego problemu, gdyż jest to obsługiwane we wszystkich obecnie stosowanych środowiskach.
    Ale żeby nieco skomplikować sprawę… zastosowanie input type=”email” może pozwolić urządzeniom mobilnym na włączenie odpowiedniego trybu klawiatury (np. z łatwiejszym dostępem do symbolu „@”).
    Kwestię jakie rozwiązanie wybrać pozostawiam więc otwartą. Myślę, że nie ma tu jednoznacznej odpowiedzi, i trzeba po prostu wybrać każdorazowo rozsądny kompromis w zależności od typu projektu i rodzaju użytkowników.

Google Analytics Alternative