What is the correct approach to return validation errors from lightning controller?
Suppose we have an aura method that takes some parameters. We need to validate these parameters in order to make sure that required values are provided, values are correct etc. When everything is correct we are fine. But what if some validations fail, how do we report them back to client?
As I see there are basically 3 options:
- Return a string with an error message.
- Return a serialized object that could hold either success or error flag + error message
- Throw an AuraHandledException.
Option #1 - Return a string with an error message##
@AuraEnabled
public static String start(String firstname, String lastname, String email) {
if (String.isBlank(firstname)) {
return 'First name is required';
}
// lastname and email validations skipped for brevity
// When validations are done we want to return some value
// that will be used in the lightning component later
String someId = '';
return someId;
}
It is simple. The only drawback of this is in case you really need to return something when an action completes successfully, you should figure out something to differentiate error message from success message since in both cases the client will receive a String.
Option #2 - Return a serialized object##
public class ActionResult {
@AuraEnabled
public String someId { get; set; }
@AuraEnabled
public String errorMessage { get; set; }
@AuraEnabled
public Boolean isSuccess { get; set; }
}
@AuraEnabled
public static ActionResult start(String firstname, String lastname, String email) {
ActionResult result = new ActionResult();
if (String.isBlank(firstname)) {
result.isSuccess = false;
result.errorMessage = 'First name is required';
return result;
}
// lastname and email validations skipped for brevity
// When validations are done we want to return some value
// that will be used in the lightning component later
String someId = '';
result.isSuccess = false;
result.someId = someId;
return result;
}
This is more verbose comparing to Option 1, yet it makes it easier to separate errors from success results.
Option #3 - Throw an AuraHandledException##
This is a different story and there is a whole blog post Error Handling Best Practices for Lightning and Apex
@AuraEnabled
public static String start(String firstname, String lastname, String email) {
ActionResult result = new ActionResult();
if (String.isBlank(firstname)) {
throw new AuraHandledException('First name is required');
}
// lastname and email validations skipped for brevity
// When validations are done we want to return some value
// that will be used in the lightning component later
String someId = '';
return someId;
}
While this approach works, it doesn't feel right to use exceptions to something that is not exceptional in nature. While it might be good to throw exceptions when a dml operation failed or something unusual happend, with user input we expect bad values. Also when throwing AuraHandledException exceptions writing unit tests becomes a 'funny exercise':
@IsTest
static void startTest() {
// Test for null
try {
UPST_SelfRegisterFormController.initSelfRegistration(null, 'lastName', 'email');
} catch (Exception ex) {
System.assertEquals('First name is required', ex.getMessage());
}
// Test for an empty string
try {
UPST_SelfRegisterFormController.initSelfRegistration('', 'lastName', 'email');
} catch (Exception ex) {
System.assertEquals('First name is required', ex.getMessage());
}
// Same tests for last name and email
// ...
}
At yet one more thing, in order to actually see the exception message in unit test it should be set up a bit differently (Testing AuraHandledExceptions):
if (String.isBlank(firstname)) {
String message = 'First name is required';
AuraHandledException e = new AuraHandledException(message);
e.setMessage(message);
throw e;
}