Search
  • Tony Nelson

How to Handle Apex Errors For Lightning Component Implementations



Happy holidays! I hope you are doing well. We just had the largest IT event in Japan, the Salesforce World Tour Tokyo. It’s like the Japanese version of Dreamforce and TerraSky sponsored as a Platinum partner.  I was invited to give a quick Lightning talk at the Developer Zone.


Anyway, let’s start. I would like to talk about the error handling of Server-side (Apex) processing with Lightning.


How to Call the Server Side Processing From Lightning

Lightning is a framework that is optimized for SPA. It basically uses Controller.js and Helper.js to process on the client-side and Apex to run things on the Server-side like update records on the Object.


Specifically speaking, it uses Lightning’s $A.enqueueAction() to call ApexController method on the server-side to receive the callback of the result.


Below are some examples of the process to call Controller.js –> Apex from the component.

  1. When a button is clicked by the component.

  2. When an ApexController on the Server side is called from the Controller.js on the Client side.

  3. When the name of the using Apex UserInfo is returned.

  4. When a call-back from the Client-side is retrieved and the response is shown on the page.

Component

<aura:component controller=”SampleServerSideController”>

<aura:attribute name=”message” type=”String”/>

<ui: button label=”GO” press=”{!c.validate}”/>

<div>

ResultMessage:<ui:outputText value=”{!v.message}”/>

</div>

</aura:component>


Controller.js

({

validate : function(cmp, evt) {

var action = cmp.get(“c.getServerSideMessage”);

action.setCallback(this, function(a) {

cmp.set(“v.message”, a.getReturnValue());

});

$A.enqueueAction(action);

}

})


ApexController

public class SampleServerSideController {

@AuraEnabled

public static String getServerSideMessage(){

return UserInfo.getLastName();

}

}


Here is the result:


The ResultMessage showed the user’s name that was retrieved from the server.


What Will Happen If the Exception Occurred With Apex?

If an Exception occurred with Apex, what will happen to the Client-side? Let’s manually create an error without any error handling settings.


ApexController

public class SampleServerSideController {

@AuraEnabled

public static String getServerSideMessage(){

String str = null;

str.length(); // throw NullPointerException

return UserInfo.getLastName();

}

}

Nothing showed up on the page or on the browser’s console log even though the debug log for Apex showed that the “System.NullPointerException: Attempt to de-reference a null object” had occurred. If this was in a real-life (an official application), the users would have no idea that the error had occurred, leaving the error untouched. That is the worst.


Handling the Error From the Client Side

Lightning holds a message of the status and the error of the callback result from the server-side. Let’s show those messages on the page.


Controller.js

({

validate : function(cmp, evt) {

var action = cmp.get(“c.getServerSideMessage”);

action.setCallback(this, function(a) {

if (a.getState() === “SUCCESS”) {

cmp.set(“v.message”, a.getReturnValue());

} else if (a.getState() === “ERROR”){

var errors = action.getError();

if (errors) {

if (errors[0] && errors[0].message) {

cmp.set(“v.message”, errors[0].message);

}

}

}

});

$A.enqueueAction(action);

}

})

This time, the Error message is shown on the page. NullPointerException should have occurred with the Apex Exception. However, the message is shown as an Internal Server Error. I don’t know why this had happened but I guess it is a specification of Lightning to return everything as the “Internal Server Error” with all Exceptions on the Server-side system. Let’s see what will happen with the DMLException.


ApexController

public class SampleServerSideController {

@AuraEnabled

public static String getServerSideMessage(){

Account acc = new Account();

insert acc; // throw DMLException(required Name input is left blank)

return UserInfo.getLastName();

}

}

Again, nothing showed up on the page. It even looked up the Error Status in the Controller.js to insert the error message. I will use a console log to take a look inside the Error Object returned by the callback to find out why.

The error message was retrieved by errors[0].message at NullPointerAssignment. However, it seems as if the structure of the Error Object is different from the DMLException.


DML will return errors for multiple records, and there are errors for multiple numbers of fields in one record. It seems like those items are in the pageErrors or fieldErrors. Let’s create a Controller.js with that in mind.


Controller.js

({

validate : function(cmp, evt) {

var action = cmp.get(“c.getServerSideMessage”);

action.setCallback(this, function(a) {

if (a.getState() === “SUCCESS”) {

cmp.set(“v.message”, a.getReturnValue());

} else if (a.getState() === “ERROR”){

var errors = action.getError();

if (errors) {

if (errors[0] && errors[0].message) {

// System Error

cmp.set(“v.message”, errors[0].message);

} else if (errors[0] && errors[0].pageErrors) {

// DML Error

// (This sample code is corner-cutting. It does not consider the errors in multiple records and fields.)

cmp.set(“v.message”, errors[0].pageErrors[0].message);

}

}

}

});

$A.enqueueAction(action);

}

})

The DML error message is shown correctly. It does not say “Internal Server Error” either, unlike the System Exception. Through this research, I have discovered the following as a fact:

  • The Server-side system Exception returns “Internal Server Error”.

  • DML Exception will return a correct error message.

  • The structure of the Error Object for System Exceptions and DML Exceptions differ.


What Now?

So, up to here was just a long preface. This is what I wanted to share through my blog. There are situations where a detailed error message (e.g. “Internal Server Error”) does not get returned from the Client-Side Error handling. Even though DML Error can be retrieved, it is still a mystery to understand the conditions of when the error will be defined as an Internal Server Error. I could not find any resources that explain the structure of the Error Objects. Since Lightning is still a new technology, there is no official statement of what the Error handling of Lightning should be. Considering that, I think the Exception on the Server Side should be handled properly and return the value. I guess it is an ordinary thing to do try-catch on the Server Side.


Here is the code.


ApexController

public class SampleServerSideController {

@AuraEnabled

public static ResponseDto getServerSideMessage(){

try{

// some kind of processing

} catch(Exception e){

// Insert error into the Object for Response

ResponseDto res = new ResponseDto(false, ‘System Error has occurred:’ + e.getMessage());

return res;

}

// Create Object for response as Success Completion. Insert the return value to Map

ResponseDto res = new ResponseDto(true, ”);

res.values.put(‘lastName’, UserInfo.getLastName());

return res;

}

public class ResponseDto {

@AuraEnabled public Boolean isSuccess { get; set; }

@AuraEnabled public String message { get; set; }

@AuraEnabled public Map<Object, Object> values { get; set; }

public ResponseDto(Boolean isSuccess, String msg){

this.isSuccess = isSuccess;

this.message = msg;

this.values = new Map<Object, Object>();}


Controller.js

({

validate : function(cmp, evt) {

var action = cmp.get(“c.getServerSideMessage”);

action.setCallback(this, function(a) {

// Retrieve Response Object

var response = a.getReturnValue();

if (action.getState() == “SUCCESS” && response.isSuccess) {

// Success completion

cmp.set(“v.message”, response.values.lastName);

} else if (action.getState() == “ERROR” || !response.isSuccess) {

var errors = a.getError();

if (errors[0] && errors[0].message) {

// Did not catch on the Server Side

cmp.set(“v.message”, errors[0].message);

} else if ( !response.isSuccess ) {

// Did catch on the Server Side

cmp.set(“v.message”, response.message);

}

}

});

$A.enqueueAction(action);

}

})


Create an internal Class for the return value in the Apex side. Create a new object and return with the necessary information on error or at successful completion. Please note that you will need to put the keyword @AuraEnabled when you are going to access fields in the Internal Class from the Lightning Side.


The Controller.js receives the return value at callback and decides on Successful Completion or Abnormal Completion. One thing to consider is what to do when the Error is not picked up on the Apex side. It may happen if the Apex side forgets the error handling or at a Governor Limit (or Assertion error) which is unnoticeable. Unfortunately, the Governor Limit is unnoticeable as well as it is treated as the “Internal Server Error”, leaving no one to know the error in detail. I can only think of putting it in a debug log to find its information in detail.


I did not consider much about the DML error handling when I did the coding. I suggest you to use insert/update for Database method instead of DML statement to retrieve the error information in detail and be responsive to it.


Summary

With Lightning, instead of using Controller.js to pick up the errors that are thrown from the Apex, I suggest you set the error handling on the Server-side and return the result in a responsive way.

Setting the adequate error processing will help lead the end-users to the next action. I advise you to design the error handling, what to show and not to show on the page. Thanks for reading!

107 views

© 2019 TerraSky. All Rights Reserved, TerraSky, Inc.