'How to validate an email address in Go
I have checked the StackOverflow and couldn't find any question that answers how to validate email in Go Language.
After some research, I figured out and solved it as per my need.
I have this regex and Go function, which work fine:
import (
"fmt"
"regexp"
)
func main() {
fmt.Println(isEmailValid("[email protected]")) // true
fmt.Println(isEmailValid("[email protected]")) // true -- expected "false"
}
// isEmailValid checks if the email provided is valid by regex.
func isEmailValid(e string) bool {
emailRegex := regexp.MustCompile("^[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])?)*$")
return emailRegex.MatchString(e)
}
The problem is that it accepts the special characters that I don't want. I tried to use some from other languages' "regex" expression, but it throws the error "unknown escape" in debug.
Could anyone give me a good regex or any fast solution (pkg) that works with GoLang?
Solution 1:[1]
The standard lib has email parsing and validation built in, simply use: mail.ParseAddress()
.
A simple "is-valid" test:
func valid(email string) bool {
_, err := mail.ParseAddress(email)
return err == nil
}
Testing it:
for _, email := range []string{
"[email protected]",
"bad-example",
} {
fmt.Printf("%18s valid: %t\n", email, valid(email))
}
Which outputs (try it on the Go Playground):
[email protected] valid: true
bad-example valid: false
Solution 2:[2]
The above approach from @icza is nice, however, if we use it in login/signup form validation, many people will enter a partial or incorrect email address, which will create a bunch of invalid records in production. Furthermore, who knows we might get killed because of that?.
Therefore, I went to the regex solution to validate standard emails:
Here is the code:
func isEmailValid(e string) bool {
emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
return emailRegex.MatchString(e)
}
Test Cases:
fmt.Println(isEmailValid("[email protected]")) // true
fmt.Println(isEmailValid("bad-email")) // false
fmt.Println(isEmailValid("[email protected]")) // false
fmt.Println(isEmailValid("test-email.com")) // false
fmt.Println(isEmailValid("[email protected]")) // true
Solution 3:[3]
Method implementation example:
var emailRegexp = regexp.MustCompile("^[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])?)*$")
This was in the same package as where I created my struct
type EmailInputRegistration struct {
Email string
}
And then for handling errors:
func (in EmailInputRegistration) Validate() error {
if !emailRegexp.MatchString(in.Email) {
return fmt.Errorf("%w: email invalid", ErrValidation)
}
//any other exception handling...
return nil
}
Ideally, this EmailInputRegistration should be refactored to include all the data needed for Registering such as email, user, password, etc.
Solution 4:[4]
Since I find regexp hard to read I prefer readable boring code. For example:
// Accepts at least the [email protected] pattern.
func isEmailAddress(v string) bool {
if v == "" {
return false
}
if containsWhitespace(v) {
return false
}
iAt := strings.IndexByte(v, '@')
if iAt == -1 {
return false
}
localPart := v[:iAt]
if localPart == "" {
return false
}
domain := v[iAt+1:]
if domain == "" {
return false
}
iDot := strings.IndexByte(domain, '.')
if iDot == -1 || iDot == 0 || iDot == len(domain)-1 {
return false
}
if strings.Index(domain, "..") != -1 {
return false
}
iTLD := strings.LastIndexByte(domain, '.')
return 2 <= len([]rune(domain[iTLD+1:]))
}
func containsWhitespace(v string) bool {
for _, r := range v {
if unicode.IsSpace(r) {
return true
}
}
return false
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | Shah |
Solution 4 | pepe |