6

So I have the need to surface an exception that currently gets throw 2 classes away from an @AuraEnabled method. The basic flow of the execution cycle goes like this:

OAuth Class(REST callout to 3rd party) -> Service Class(Handles parsed HTTP Response -> Lightning Apex Controller(@AuraEnabled, returns the data).

If the OAuth Class return a 429 error code for example(rate limit), it throws a custom exception, and houses the error code/message from the web service. How can I surface that code/message from the OAuth Class up to the AuraHandledException in the @AuraEnabled class?

Some codes:

OAuth Class:
//Callout happens, response comes back

if (res.getStatusCode() > 299) {
          throw new PNR_OAuthException('Failed to retrieve data. HTTP Code: ' + res.getStatusCode() + '. Message: ' + res.getStatus() + '. Response Body: ' + res.getBody());
}

Service Class:

try {
//Run method from OAuth class to get data back

} catch (Exception e) {
  throw new ServiceException(e);
}

Lightning Apex Controller:

try {
//Run service class method
} catch (Exception e) {
      throw new AuraHandledException('Example error message');
}
2

At each level of your exception handling, you can unpack the message from the caught exception and re-package it for the next handler to catch and propagate. At the @AuraEnabled level, you can then supply that message to the constructor of AuraHandledException, which will result in it being available in your component's response object, passed to its callback.

Here's an example that I think fairly well represents your situation. There's three levels of indirection in Apex, where each catches the exception thrown by the lower level and repackages its message. In the Lightning JavaScript controller, we surface the error message, unpacked from the getErrors() method on the callback response object.

enter image description here

TestAuraHandledApplication.app

<aura:application controller="TestAuraHandledExcServerController">
    <lightning:button label="Do The Thing" onclick="{! c.doTheThing }" />
</aura:application>

TestAuraHandledApplicationController.js

({
    doTheThing : function(component, event, helper) {
        var action = component.get('c.tryAnError');
        action.setCallback(this, function(response) {
            alert('I got back the response state ' + response.getState() + ' and the error message ' + response.getError()[0].message);
        });
        $A.enqueueAction(action);
    }
})

TestAuraHandledExcServerController.apxc

public class TestAuraHandledExcServerController {
    public class NotAuraHandledException extends Exception {}
    public class OtherNotAuraHandledException extends Exception {}
    public class InnerClassThrowsExc {
        public void throwException() {
            // It does not matter here whether we cause a DML error or a divide-by-zero,
            // where the system generates the message, or if we explicitly construct an exception with a message.
            throw new NotAuraHandledException('Example message');
            // or
            System.debug(1/0);
        }
    }

    public class OtherInnerClassHandlesIt {
        public void makeACall() {
            try {
                new InnerClassThrowsExc().throwException();
            } catch (Exception e) {
                throw new OtherNotAuraHandledException(e.getMessage());
            }
        }
    }

    @AuraEnabled
    public static void tryAnError() {
        try {
            new OtherInnerClassHandlesIt().makeACall();
        } catch (Exception e) {
            throw new AuraHandledException(e.getMessage());
        }
    }
}
| improve this answer | |
  • Thanks David, this worked well for me, I am not sure why I couldn't get the right combination of wrapping/unwrapping messages before, but alas, the job is done! – Stas Christiansen Sep 5 '18 at 23:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.