3

I'm trying to post some data via ajax and get a response back indicating if the post worked correctly, however I'm running into some issues.

I have a visualforce page called updateLog (/apex/updateLog) and it has the folliwing markup:

<apex:page controller="WatcherController" contentType="text/javascript" showHeader="false" action="{!updateWatcherLog}">{!updateWatcherLogResponse}</apex:page>

updateWatcherLogResponse is a JSON serialized Map of values that I would like returned as a response to the ajax request.

The following is the javascript from which I'm making the ajax request:

console.log("about to send log...");
var params = {"page":window.location.href};
var request = new XMLHttpRequest();
request.onreadystatechange = function() {  
  console.log(request.readyState);  
  if (request.readyState == 4) {
    console.log("request finished, response: " + request.response);    
  } else {
    console.log("request state changed to: " + request.readyState);
  }
}
request.open("POST","/apex/updateLog",true);
request.setRequestHeader("Content-Type", "application/json");
request.send(JSON.stringify(params));

When readyState equals 4 (The request is finished), I'm printing out the returned response, which always is an empty string. As a side note, whenever I set onerror for the request, the function seems to run very quickly, indicating that there may be an error with the way I'm setting it up and the request might not be sent at all (My System.debug log statements are also not being hit whenever I make a request using the above javascript as further evidence of this).

I can't use jQuery, Dojo, or any other 3rd party libraries for this, and RemoteActions are not a possibility. Any help would be greatly appreciated!

EDIT:

Here is a sanitized version of the controller

public class WatcherController {

    public String updateWatcherLogResponse {get; set;}

    public WatcherController() {
        //Not important
    }

    public void updateWatcherLog() {
        String page = ApexPages.currentPage().getParameters().get('page');
        System.debug('got here, page equals: ' + page);
        System.debug(ApexPages.currentPage().getParameters());
        Map<String,boolean> result = new map<String,boolean>();
        try {
            User currUser = [SELECT Id FROM User WHERE id =: UserInfo.getUserId()];
            currUser.Last_Page_Viewed__c = page;
            currUser.Last_Page_Viewed_Date_Time__c = DateTime.now();
            update currUser;
            result.put('result',true);
        } catch(Exception ex) {
            System.debug(ex.getMessage());
            result.put('result',false);
        }
        updateWatcherLogResponse = JSON.serialize(result);                                     
    }
}
  • 1
    What browser are you using? – Bob Roberts Jun 30 '13 at 22:28
  • Chrome, although optimally I'd like it to work in all browsers. – Kyle Jun 30 '13 at 22:32
  • 1
    Well, I cant see anything invalid with your request. Could you post your controller (or a sanitized version of it)? – Bob Roberts Jun 30 '13 at 22:58
  • 1
    Just posted a sanitized version of the controller – Kyle Jun 30 '13 at 23:09
  • 1
    Where's the JavaScript hosted? I'd be willing to bet if you're using a sidebar component that you'll run into cross-domain security issues, since Apex and the core system reside on different domain names. – sfdcfox Jul 1 '13 at 4:23
1

Have you tried a commandLink with parameters (and reRender if needed) to achieve your goals? While this doesn't answer your stated question of why the ajax doesn't work, it does give you a way to asynchronously send parameters and retrieve results without use of RemoteActions or 3rd party libraries.

If, you need to use this function programaticaly, there is always actionFunction. This gives you a javascript method you can call like any other.

  • So I don't want to use a link/button since this code needs to run in the background. Also, the javascript is located in a side bar component, so I don't think I can use actionFunctions or commandLinks. – Kyle Jun 30 '13 at 23:58
1

I am using Chrome as well, and as I expected, there is a cross-domain problem happening here. I created a page called /apex/ajax, which I'm calling from both a sidebar component and a page called /apex/demo. I tried this in two different organizations as well. On my na1.salesforce.com organization, Visualforce runs on the same domain as the rest of the instance (bug?), and the request went through just fine.

I then tested this in my namespaced, My Domain'd dev org, I got the expected result. On my home page, when ready state reached 4, the status was 0 and there was no response. The Network log in Chrome showed the request as "(canceled)" and was highlighted red. When accessed from /apex/demo, I got the usual response. In other words, this is definitely a cross-site issue. I even tried to get around it by having /apex/ajax emit the script itself using /apex/ajax?script=1 (in an obviously modified controller), and the same result.

So, it turns out that you probably need to use SOA (salesforce.com's JavaScript proxy) in order to make this happen. Here's a little snippet I cooked up for this purpose:

<script src="/soap/ajax/28.0/connection.js"></script><script>sforce.connection.sessionId = document.cookie.match(/sid=([^;]+)/)[1];sforce.connection.remoteFunction({ url: "https://my-domain--visual.force.com/apex/ajax", onSuccess: function(result) { console.debug(result); }, onFailure: function(result) { console.debug(result); } });</script>

This required a Remote Site set up under "Network Access", and it still was giving me fits (Read timed out), but this architecture should work under normal conditions. Ironic having to proxy from salesforce.com to salesforce.com, but there you have it. You'll have a much easier time of it if you just "cheat" by not using AJAX at all. Consider the following:

<script>var src="/apex/ajax?page="+window.location.href, e = new document.createElement("script"); e.src = src; document.body.appendChild(e);</script>

Your actual Visualforce page could return nothing (or some debug output value), keeping in mind that the point isn't really to retrieve a script, but to trick the browser into logging the data by allowing the cross-site request. You could also just use an img tag, but this is less noticeable.

This brings me to a side note; your page wasn't going to work as written anyways. Visualforce can't take a JSON payload like that and process it. PageReference.getParameters() returns a map of URL name-value pairs, nothing more. So, you should just pass the parameters using a GET instead, and pass them on the URL query string.

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.