[SalesForce] Empty responses to Lightning Component Actions

EDIT: 25/03/2017 – reworked example to most basic apex class, serialised using JSON and simple string.

We've suddenly (today) started seeing issues in one of our orgs with lightning components that make server side requests that return Apex classes, in that the responses are empty. I've got this down to a really simple test case that shows the difference between returning an apex class that contains a String, a serialised apex class and a simple String.

Apex Controller:

public class KeirTestCtrl {
    @AuraEnabled
    public static KeirResult GetClass()
    {
        KeirResult res=new KeirResult();
        res.message='All good';
        System.debug('Returning ' + res);
        return res;
    }

    @AuraEnabled
    public static String GetSerialisedClass()
    {
        KeirResult res=new KeirResult();
        res.message='All good';
        String resStr=JSON.serialize(res);
        System.debug('Returning ' + resStr);
        return resStr;
    }

    @AuraEnabled
    public static String GetString()
    {
        String res='All Good';
        System.debug('Returning ' + res);
        return res;
    }
}

Apex Class returned by the controller:

public class KeirResult
{
    public KeirResult() {}

    @AuraEnabled
    public String message {get; set;}
}

Lightning Application:

<aura:application controller="KeirTestCtrl">
    <button onclick="{!c.getClass}">Get the Class</button><br/>
    <button onclick="{!c.getSerialisedClass}">Get serialised class</button><br/>
    <button onclick="{!c.getString}">Get the String</button><br/>
</aura:application>

Lightning Controller:

({
    getClass : function(component, event, helper) {
        helper.getClass(component, event);
    },
    getSerialisedClass : function(component, event, helper) {
        helper.getSerialisedClass(component, event);
    },
    getString : function(component, event, helper) {
        helper.getString(component, event);
    }
})

Lightning Helper:

({
    getClass : function(cmp, ev) {
        var action=cmp.get('c.GetClass');
        var self=this;
        action.setCallback(this, function(response) {
                self.actionResponseHandler(response);
        });
        $A.enqueueAction(action);
    },
    getSerialisedClass : function(cmp, ev) {
        var action=cmp.get('c.GetSerialisedClass');
        var self=this;
        action.setCallback(this, function(response) {
                self.actionResponseHandler(response);
        });
        $A.enqueueAction(action);
    },
    getString : function(cmp, ev) {
        var action=cmp.get('c.GetString');
        var self=this;
        action.setCallback(this, function(response) {
                self.actionResponseHandler(response);
        });
        $A.enqueueAction(action);
    },
    actionResponseHandler : function (response) {
        try {
            var state = response.getState();
            console.log('State = ' + state);
            if (state === "SUCCESS") {
                var retVal=response.getReturnValue();
                alert('Result = ' + JSON.stringify(retVal));
            }
            else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        alert("Error message: " + errors[0].message);
                    }
                } 
                else {
                    alert("Unknown error");
                }
            }
        }
        catch (e) {
            alert('Exception in actionResponseHandler: ' + e);
        }
    }
})

When I run this on my personal dev org on the EU5 instance and click the 'Get Class' button, I get the following alert in the JavaScript console:

Result = {"message":"All good"}

However, when I run this on our main org on the EU3 instance I get:

Result = {}

Checking the debug log shows that the response has been created correctly:

08:38:09:002 USER_DEBUG [7]|DEBUG|Returning KeirResult:[message=All
good]

If I click the button to return the serialised class on my main org, I get the following as expected:

Result = "{\"message\":\"All good\"}"

and when returning a simple string:

Result = "All Good"

As far as I can tell, everything that returns an apex class as a response in the EU3 org is now returning empty values, which means the majority of my lightning components, and thus most of my custom functionality, no longer work. I've raised a case with Salesforce support (16080817) but I'm curious if anyone else has seen this as it seems pretty fundamental.

Best Answer

This should be covered by https://success.salesforce.com/issues_view?title=auraenabled-annotation-not-working-correctly-at-runtime-for-json-serialze&Id=a1p3A0000008gca (which links to this post as an example).

The KI talks about JSON.serialize, rather than the implicit serialization of returning an Object from an @AuraEnabled method as you're doing here, but the underlying bug should fix your example here when rolled out.

Also note from the KI: "Please contact Salesforce support if an immediate fix is needed.", if you do this reference the KI so they can fast track the resolution that's internally linked to the bug.