0

I have a controller extension that makes a get request inside the constructor to get some information from an api.

public extension(ApexPages.StandardController stdController) {
    Account accountRecord = (Account) stdController.getRecord();
    accountInfo = [SELECT Id, Name FROM Account WHERE Id = :accountRecord.Id];

    // Required to render the infomation when user click on the button
    getInfosFromApi(accountInfo.Name);
}

private HttpResponse getInfoFromApi(String name) {
    try {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setMethod('GET');
        request.setEndpoint(getUri(name));
        HttpResponse response = http.send(request);
        // Use this response to render some information on the VF page
        return response;
    } catch (Exception error) {
        // Just get the error message and save it in the custom object.
        SaveException.log(error); // Here the error occurs, because I can't use a dml operation inside the constructor directly or indirectly.
        return null;
    }
}

When I click to access the page to test if the error is being saved I get the following error: "DML currently not allowed"

I really need that the HTTP request be called when I click on the page to render the information from the api first, but I need to save the information in this custom object too if any kind of error happens. Any ideas?

thanks in advance!

7
  • Did you try a page action method?
    – cropredy
    Commented Jun 21, 2022 at 7:50
  • Yes, but I catch the error in the HTTP request, and I need that HTTP request to run in the constructor. So if I use action method, that page is not rendered with the information from the api. Commented Jun 21, 2022 at 14:30
  • The page action method runs immediately after the constructor and http request can be done there
    – cropredy
    Commented Jun 21, 2022 at 15:07
  • And how can I re-render the page with the information from the API after the action method run? It's a simple page that only show the information from the api. Commented Jun 21, 2022 at 15:31
  • I apologize for the page action method approach as it isn't 100% reliable - Bob Buzzard, the king of VF development suggests this approach: bobbuzzard.blogspot.com/2011/08/dml-during-initialisation.html
    – cropredy
    Commented Jun 21, 2022 at 20:32

1 Answer 1

0

There are a couple of approaches to solve this

Use a page action method to do the Http callout.

However, the VF manual recommends against doing this:

The action method invoked when this page is requested by the server. Use expression language to reference an action method. For example, action="{!doAction}" references the doAction() method in the controller. If an action isn’t specified, the page loads as usual. If the action method returns null, the page simply refreshes.

This method is called before the page is rendered, and allows you to optionally redirect the user to another page.

Important: Do not use this action for initialization or DML.

Jill Wetzler, who was the original VF Developer Liaison also recommends against doing this

However, you will find references to this approach (example) so YMMV

Use JS to invoke a controller action method

This comes from Keir Bowden aka Bob Buzzard, author of the Visualforce Development Cookbook. A window.onload() event handler invokes the controller's action method to do initialization.

Here, I'm quoting directly from his blog post

VF Apex controller

public class InsertObjectController 
{
 public Boolean initialised{get; set;}
 public Account acc {get; set;}
  
 public InsertObjectController()
 {
  initialised=false;
 }
  
 public void init()
 {
  if (!initialised)
  {
   acc=new Account();
   acc.Name='Blog Account';
   insert acc;
   initialised=true;
  }
 }
}

VF page

<apex:page controller="InsertObjectController">
  <apex:form >
    <apex:actionFunction name="doInit" action="{!init}" rerender="allPanel"/>
    <apex:outputPanel id="allPanel">
      <apex:outputPanel rendered="{!NOT(initialised)}">
        <p align="center" style='{font-family:"Arial", Helvetica, sans-serif; font-size:20px;}'><apex:image value="/img/loading.gif"/>&nbsp;Please wait</p>
        <script>
           window.onload=function()
           {
             doInit();
           };
        </script>
      </apex:outputPanel>
      <apex:outputPanel rendered="{!initialised}">
        <apex:pageBlock title="Account Details">
          <apex:pageBlockSection >
            <apex:inputField value="{!acc.id}"/>
            <apex:inputField value="{!acc.name}"/>
            <apex:inputField value="{!acc.type}"/>
            <apex:inputField value="{!acc.industry}"/>
          </apex:pageBlockSection>
        </apex:pageBlock>
      </apex:outputPanel>
    </apex:outputPanel>
  </apex:form>
</apex:page>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .