Getting “Unexpected response data from server” error using SharePoint JSOM?

I was clearing the dust off an old demo for my session on cloud-hosted apps at SharePointFest DC next month when I ran into a problem. As I walked through the demo (which I had given three times before at SPS Events in Boston, Baltimore, and DC), I was getting an error when testing my ability to query for list data in my cloud-hosted app’s app web in my Office 365 developer tenant using JSOM:jsrequestfailedKnowing this script had worked for months in the past, I turned to the Internet. Sure enough, others appeared to be experiencing the same problem. One app developer even worked around the problem by abandoning the use of JSOM altogether in favor of the REST APIs.

What’s the problem here?

In a cloud-hosted app, the app’s business logic is deployed to a remote web (separate from its app web, which resides in SharePoint). This remote web is likely on a different domain (and is definitely on a different domain if you are using SharePoint Online). Querying for data that resides in the app web from a script running in your remote web requires the use of cross-domain JavaScript calls, which are generally blocked for security reasons. To work around this requires the use of the JavaScript Cross-Domain Library (sp.requestexecutor.js).

My demo script looked like this:

var clientContext = new SP.ClientContext(appweburl);
var appWeb = clientContext.get_web();

appWebList = appWeb.get_lists().getByTitle(listname);
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml("50");
appWebListItems = appWebList.getItems(camlQuery);
clientContext.load(appWebList);
clientContext.load(appWebListItems);
clientContext.executeQueryAsync(onAppWebGetListDataSuccess, onJSOMError);

Once I initialized the ProxyWebRequestExecutorFactory object and set it as the factory of my ClientContext object, everything worked as expected. (NOTE: The script in my demo that queried for data in the host web was already making proper use of the cross-domain library.)

Here is the proper script to initialize the ClientContext and associate the appropriate ProxyWebRequestExecutorFactory with it:

var clientContext = new SP.ClientContext(appweburl);
var factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
clientContext.set_webRequestExecutorFactory(factory);
var appWeb = clientContext.get_web();

// Remaining code is the same as above

Conclusion

I’m not sure exactly what changed that would cause this problem to start occurring. Common sense dictates that the ProxyWebRequestExecutorFactory should be needed for all cross-domain calls, whether they be to the host web or the app web. I’m less concerned about that than why the old code ever worked (that did not make use of it). Nonetheless, if you find yourself in a situation where a script that definitely worked in the past now fails with an “Unexpected response data from server” error when using the JSOM, ensure you are making proper use of the cross-domain library as well.