Permalink
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upreferencesource/System.ComponentModel.DataAnnotations/DataAnnotations/EmailAddressAttribute.cs
Go to file| namespace System.ComponentModel.DataAnnotations { | |
| using System; | |
| using System.ComponentModel.DataAnnotations.Resources; | |
| using System.Text.RegularExpressions; | |
| [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] | |
| public sealed class EmailAddressAttribute : DataTypeAttribute { | |
| // This attribute provides server-side email validation equivalent to jquery validate, | |
| // and therefore shares the same regular expression. See unit tests for examples. | |
| private static Regex _regex = CreateRegEx(); | |
| public EmailAddressAttribute() | |
| : base(DataType.EmailAddress) { | |
| // DevDiv 468241: set DefaultErrorMessage not ErrorMessage, allowing user to set | |
| // ErrorMessageResourceType and ErrorMessageResourceName to use localized messages. | |
| DefaultErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid; | |
| } | |
| public override bool IsValid(object value) { | |
| if (value == null) { | |
| return true; | |
| } | |
| string valueAsString = value as string; | |
| // Use RegEx implementation if it has been created, otherwise use a non RegEx version. | |
| if (_regex != null) { | |
| return valueAsString != null && _regex.Match(valueAsString).Length > 0; | |
| } | |
| else { | |
| int atCount = 0; | |
| foreach (char c in valueAsString) { | |
| if (c == '@') { | |
| atCount++; | |
| } | |
| } | |
| return (valueAsString != null | |
| && atCount == 1 | |
| && valueAsString[0] != '@' | |
| && valueAsString[valueAsString.Length - 1] != '@'); | |
| } | |
| } | |
| private static Regex CreateRegEx() { | |
| // We only need to create the RegEx if this switch is enabled. | |
| if (AppSettings.DisableRegEx) { | |
| return null; | |
| } | |
| const string pattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$"; | |
| const RegexOptions options = RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture; | |
| // Set explicit regex match timeout, sufficient enough for email parsing | |
| // Unless the global REGEX_DEFAULT_MATCH_TIMEOUT is already set | |
| TimeSpan matchTimeout = TimeSpan.FromSeconds(2); | |
| try { | |
| if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) { | |
| return new Regex(pattern, options, matchTimeout); | |
| } | |
| } | |
| catch { | |
| // Fallback on error | |
| } | |
| // Legacy fallback (without explicit match timeout) | |
| return new Regex(pattern, options); | |
| } | |
| } | |
| } |