Thursday, April 17, 2014

Apex Email Validation

Introduction 


Email Validation is one of those things that have been around for as long as I can think, and Emails aren't going any where any time soon. So today I will show you how you can validate a bunch of Email addresses in Apex before sending out your emails. You can inform the user of any wrong email addresses and have them correct them before proceeding.

What is a valid Email address? 


The correct answer to this question is "Well it depends". The next question is probably "on what?". There has always been and there is still a long debate going on around this, at times to philosophical for my taste. I will not get into that here. All I can say is, be certain what you want your validation rules to cover and use simple regular expressions to cover them. Here is my favorite site on Regular Expressions. For the fanatics among you check this for guidance RFC 5322 - Address Specification.
What you definitely shouldn't do is try to invent a Reg Ex that covers everything - this is a fool's errand. I got slapped around a couple of times by Apex's "Regex too complicated" error. That said, let's move on to the real issue.

Apex


To send emails to a bunch of Users, you would normally pass a list of up to 100 email addresses to the  setToAddresses() method of a SingleEmailMessage() instance. Check out these post to see how your could let your users input email addresses: Add and remove email recipients dynamically & Building an email recipient list from pre-selected users

Before setting the email address list in your SingleEmailMessage instance, it is an excellent idea to validate your email address list. Use a function that looks like this:
 public List<String> validateEmailAddressPatternExample(List<String> emailAddresses){  
      List<String> wrongEmailAddresses = new List<String>();  
      String regEx = '^[a-zA-Z0-9._|\\\\%#~`=?&/$^*!}{+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$';  
      try{  
           for(String email : emailAddresses){  
                if(!Pattern.matches(regEx, email)){  
                     wrongEmailAddresses.add(email);  
                }  
           }  
      } catch(Exception e){  
           wrongEmailAddresses.add('Error occured');  
      }  
      return wrongEmailAddresses;  
 }  

Here every single email address is matched against a Pattern which is compiled from the supplied Regular Expression.

Another way to do this will be to compile the Regular Expression into a pattern object and then create a Matcher for a single email and then use the Matcher's match() method to check if it is valid.
 public List<String> validateEmailAddressMatcherExample(List<String> emailAddresses){  
      List<String> wrongEmailAddresses = new List<String>();  
      String regEx = '^[a-zA-Z0-9._|\\\\%#~`=?&/$^*!}{+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$';  
      Pattern emailPattern = Pattern.compile(regEx);  
      try{  
           for(String email : emailAddresses){  
                Matcher emailMatcher = emailPattern.matcher(email);  
                if(!emailMatcher.matches()){  
                     wrongEmailAddresses.add(email);  
                }  
           }  
      } catch(Exception e){  
           wrongEmailAddresses.add('Error occured');  
      }  
      return wrongEmailAddresses;  
 }  

Since we want to inform the User about the email addresses which are not valid, we gather them together in a list and return them to the calling function. In the calling function we could then check the list returned and inform the user that some email addresses are not valid.
 List<String> wrongEmailAddresses = this.validateEmailAddress(emailAddresses);  
 if(!wrongEmailAddresses.isEmpty()){  
      String errorMsg;  
      if(wrongEmailAddresses.size() > 1){  
           String wrongEmails = String.join(wrongEmailAddresses, ',');  
           errorMsg = 'The Opportunity Report was not distributed. Please correct the following Email addresses: ' + wrongEmails;  
           errorMsg += '<ul>';  
           for()  
      } else {  
           errorMsg = 'The Opportunity Report was not distributed. Please correct the following Email address: ' + wrongEmailAddresses[0];  
      }  
      Apexpages.Message msg = new Apexpages.Message(Apexpages.Severity.Error, errorMsg);  
      Apexpages.addMessage(msg);  
      return null;  
 }  






If only a single address is wrong, no problem, the error message is pretty basic. If many are wrong, the User gets all of them in one line as a comma separated list.



But a better way would probably be to list them one after another. This is definitely more readable. This is the improve code snippet:
 List<String> wrongEmailAddresses = this.validateEmailAddress(emailAddresses);  
 if(!wrongEmailAddresses.isEmpty()){  
      String errorMsg;  
      if(wrongEmailAddresses.size() > 1){  
           String wrongEmails = String.join(wrongEmailAddresses, ',');  
           errorMsg = 'The Opportunity Report was not distributed. Please correct the following Email addresses: ';  
           errorMsg += '<ul>';  
           for(String emailAdd : wrongEmailAddresses){  
                errorMsg += '<li>' + emailAdd + '</li>';  
           }  
           errorMsg += '</ul>';  
      } else {  
           errorMsg = 'The Opportunity Report was not distributed. Please correct the following Email address: ' + wrongEmailAddresses[0];  
      }  
      Apexpages.Message msg = new Apexpages.Message(Apexpages.Severity.Error, errorMsg);  
      Apexpages.addMessage(msg);  
      return null;  
 }  

Visualforce

Your visualforce needs to include the pageMessages tag for the error messages to be displayed. If you use HTML in your Apex code like we did above to present the wrong email addresses in the list, you need to set the escape attribute of the pageMessage component to false.
 <apex:outputPanel id="pageMessages">  
      <apex:pageMessages escape="false" />  
 </apex:outputPanel>  

Notice the the pageMessage component is wrapped in an outputPanel component. This is a common technique used which gives us the possibility to be able to refresh only the pageMessage component when our call to the controller return.
 <apex:commandButton value="Distribute Opportunity Report Now" action="{!distribute}" rerender="pageMessages"></apex:commandButton>  

Conlusion


I hope you enjoyed reading this post. To make it more useful to others viewing it in the future, i challenge you to challenge it's contents and offer alternatives. Thanks and God bless

No comments:

Post a Comment